When a network request fails, if the policy of automatic resending the request is adopted, there are two problems: the number of retries and the interval of sending the request. The number of retries and request interval varies according to application scenarios and service environments.

For how to determine the interval of requests. If the interval is too long, the software experience deteriorates and is even interrupted by the user before the connection is re-established. If the interval is too short, network congestion may occur. One way to circumvent (balance) is to balance the interval between too long and too short, that is, to dynamically let the interval grow. To achieve dynamic interval growth, you need two parameters, the basic interval (the time between first retries) and the coefficient of interval growth (which is not necessarily linear).

There are two scenarios in which the request needs to be retried. One is an error in the network request, and the other is a partial data write/read failure in a request, and the connection of the failed part needs to be re-established.

A condition in which a request fails

  • Overall logic:
const callRetry = async (callFunc, depth=0) = > {try {
        return await callFunc(); // The request is sent when the number of retries meets the requirement
    } catch(error) {
        if(depth>7) {throw error;
        }
        await wait(2**depth*100) // Asynchronous wait time
        console.log(`retry:${depth}Time `)
        return callRetry(callFunc, depth+1) // Recursive - retry}}Copy the code
  • Asynchronous delay:
const wait= (ms) = > new Promise((res= > {setTimeout(res, ms)}));
Copy the code
  • Simulate an incorrect network request:
const callFunc = (sucessProbability=0.5) = > {
    return new Promise((res, rej) = > Math.random < sucessProbability? res("success"):rej("error"))}Copy the code
  • Use:
callRetry(callFunc,1)
    .then(res= >{
        console.log(res)
    })
    .catch(error= > {
    	console.log(error)
	})
Copy the code

Request partial failure

  • Overall logic:
const callWithProgress = async (fn, status, depth = 0) = > {const result = await fn(status); // what if the request fails here

	// Check whether the request is completed
	if (result.progress === 1) {
		// Request completed
		return result.result;
	}else {
		// The request was not completed
		if (depth > 7) {
			throw result;
		}
		await wait(2 ** depth * 10);
	
		return callWithProgress(fn, result.progress, depth + 1); }}Copy the code
  • Simulate the request process:
const progressingOperation = async (startProgress = 0) = > {await wait(10);
	const progress = Math.round(Math.min(startProgress + Math.random() / 3.1) * 10) / 10;
	return {
		progress,
		result: progress === 1 ? "result" : undefined}; }Copy the code
  • Call:
callWithProgress(progressingOperation).then(res= >console.log(res)).catch(error= >console.log(error))
Copy the code