How to use promise Race and promise any?

What is Promise?
Definition of Promise on MDN: Promise object is used to represent the final completion (or failure) of an asynchronous operation and its result value. This may sound a little too complicated for novices.

A big what abroad explained to Promises as follows: "imagine that you are a child. Your mother promised you that she would buy you a new mobile phone next week."

You won't know whether you can get that phone until next week. Your mother either really bought you a brand-new mobile phone, or she won't buy it for you because she's unhappy.

This is a Promise. A Promise has three states. namely:

Pending: you don't know if you can get that phone
Fulfilled: Mom is happy and bought it for you
Rejected: I'm not Kaisen anymore. I won't buy it for you
This is the fastest I can understand Promise case I have heard so far.

If you haven't started learning Promise, I suggest you do so.

Promise contains several very useful built-in methods. Today we mainly introduce these two methods.

Promise.race() - released with ES6
Promise.any() - still in stage 4 proposal
Promise.race()
Promise. The race () method was originally released when promise was introduced in ES6. This method requires an iterable as a parameter.

Promise. The race (Iterable) method returns a promise. Once a promise in the iterator is resolved or rejected, the returned promise will be resolved or rejected.

With Promise Any() method is different, Promise The race () method focuses on whether Promise has been resolved, regardless of whether it has been resolved or rejected.

grammar
Promise.race(iterable)
1
parameter
Iteratable - iteratable object, similar to Array. The iterable object implements symbol Iterator method.

Return value
As long as a promise in a given iteration is resolved or rejected, a pending promise takes the value of the first promise as its value, so as to resolve or reject asynchronously (once the stack is empty).

be careful
Because the parameter accepts iterable, we can pass some values, such as basic values and even objects in the array. In this case, the race method returns the first non promise object passed. This is mainly because the behavior of the method is to return the value immediately when the value is available (when promise is satisfied).

In addition, if a resolved Promise is passed in Iterable, Promise The race () method resolves to the first of the values. If an empty iteratable is passed, the race method will always be in the pending state.

example

const promise1 = new Promise((resolve, reject) => {     
	setTimeout(resolve, 500, 'promise 1 resolved'); });  
	const promise2 = new Promise((resolve, reject) => {     
		setTimeout(reject, 100, 'promise 2 rejected'); 
	});  
	const promise3 = new Promise((resolve, reject) => {     
		setTimeout(resolve, 200, 'promise 3 resolved') });  
	(async () => {     
		try {         
			let result = await Promise.race([promise1, promise2, promise3]);         
			console.log(result);     
		} catch (err) {         
			console.error(err);     
		} 
	})();   // Output - "promise 2 rejected" / / although promise 1 and promise 3 can be solved, promise 2 rejects faster than them// So promise The race method will be rejected with promise2 

1
Real use case
Now, you may want to know when we promise race() ? Let's see.

Displays the load animation when data is requested

Using load animation is very common in development. When the data response time is long, if the loading animation is not used, it looks like there is no response. But sometimes, the response is too fast. When we need to load the animation, we add a very small delay time, which will make the user feel that I am often requesting it. To do this, just use promise Race () method, as shown below.

function getUserInfo(user) {
return new Promise((resolve, reject) => { // had it at 1500 to be more true-to-life, but 900 is better for testing
setTimeout(() => resolve("user data!"), Math.floor(900*Math.random()));
});
}
function showUserInfo(user) {
return getUserInfo().then(info => {
console.log("user info:", info); return true;
});
}
function showSpinner() {
console.log("please wait...")
}
function timeout(delay, result) {
return new Promise(resolve => {
setTimeout(() => resolve(result), delay);
});
}
Promise.race([showUserInfo(), timeout(300)]).then(displayed => { if (!displayed) showSpinner(); });
1
**Cancel Promise**

In some cases, we need to cancel Promise. In this case, we can also use Promise Race() method:

function timeout(delay) {   
	let cancel;   
	const wait = new Promise(resolve => {     
		const timer = setTimeout(() => resolve(false), delay);     
		cancel = () => {       
			clearTimeout(timer);       
			resolve(true);     
		};   
	});   
	wait.cancel = cancel;   
	return wait; 
}   
function doWork() {   
	const workFactor = Math.floor(600*Math.random());   
	const work = timeout(workFactor);      
	const result = work.then(canceled => {     
		if (canceled)       console.log('Work canceled');     
		else       console.log('Work done in', workFactor, 'ms');     
		return !canceled;   
	});   
	result.cancel = work.cancel;   
	return result; 
}  
function attemptWork() {   
	const work = doWork();   
	return Promise.race([work, timeout(300)]) .then(done => {       
		if (!done)         work.cancel();       
		return (done ? 'Work complete!' : 'I gave up');   
	}); 
}  
attemptWork().then(console.log); 

1
Batch request for long execution

Chris Jensen has an interesting race() method use case. He used promise The race() method batches long-running requests. In this way, they can keep the number of parallel requests fixed.

const _ = require('lodash')  
async function batchRequests(options) {     
	let query = { offset: 0, limit: options.limit };      
	do {         
		batch = await model.findAll(query);         
		query.offset += options.limit;          
		if (batch.length) {             
			const promise = doLongRequestForBatch(batch).then(() => {                 
				// Once complete, pop this promise from our array                 
				// so that we know we can add another batch in its place                
				 _.remove(promises, p => p === promise);             
		 });             
		 promises.push(promise);              
		 // Once we hit our concurrency limit, wait for at least one promise to             
		 // resolve before continuing to batch off requests             
		 if (promises.length >= options.concurrentBatches) {                 
		 	await Promise.race(promises);             
	 	}         
 	}     
} 
while (batch.length);      
	// Wait for remaining batches to finish     
return Promise.all(promises); 
}  
batchRequests({ limit: 100, concurrentBatches: 5 }); 

1
Promise.any()
Promise.any() receives a promise iteratable object. As long as one of the promises is successful, it returns the successful promise. If none of the promises in the iteratable object succeeds (that is, all the promises fail / reject), an instance of failed promise and AggregateError is returned. It is a subclass of Error and is used to collect single errors. In essence, this method is similar to promise All () is the opposite.

be careful! Promise. The any () method is still experimental and not yet fully supported by all browsers. It is currently in the fourth stage draft of TC39 (Stage 4)

grammar
Promise.any(iterable);
1
parameter
Iteratable - an iteratable object, such as Array.

Return value
If the passed in parameter is an empty iteratable object, a Promise with an already rejected state is returned.
If the parameter passed in does not contain any promise, a promise of * * asynchronously resolved) * * is returned.
In other cases, a pending promise will be returned. As long as any promise in the incoming iteration object becomes a resolved state, or all the promises fail, the returned promise will become a resolved / rejected state asynchronously (when the call stack is empty).
explain
This method is used to return the first successful promise. As long as one promise succeeds, this method will terminate, and it will not wait for all other promises to complete.

Unlike promise All () will return a set of resolved values. In that case, we can only get a success value (assuming that at least one promise is completed). This method is useful when we only need one promise success and don't care which one is successful.

At the same time, it's not like promise Race () always returns the first result value (resolved/reject), and this method returns the first successful value. This method will ignore all rejected promises until the first promise succeeds.

example

const promise1 = new Promise((resolve, reject) => {   setTimeout(reject, 100, 'promise 1 rejected'); });  const promise2 = new Promise((resolve, reject) => {   
	setTimeout(resolve, 400, 'promise 2 resolved at 400 ms'); 
});  
const promise3 = new Promise((resolve, reject) => {   
	setTimeout(resolve, 700, 'promise 3 resolved at 800 ms'); 
});  
(async () => {   
	try {     
		let value = await Promise.any([promise1, promise2, promise3]);     
		console.log(value);   
	} catch (error) {    
		 console.log(error);   
	 } 
})();  //Output - "promise 2 resolved at 400 ms" 

1
Notice promise from the above code Any () focuses on parsed values. It ignores promise1 rejected at 100 milliseconds and takes into account the value of promise2 resolved after 400 milliseconds.

Real use case
Retrieve resources from the fastest server

It is assumed that the users visiting our website may come from all over the world. If our server is based on a single location, the response time will vary according to the location of each user. However, if we have multiple servers, we can use the server that can produce the fastest response. In this case, promise The any () method receives a response from the fastest server.

Added by yuws on Tue, 18 Jan 2022 13:14:57 +0200