16. Promise Object (Introduction to ES6-Ruan Yifeng)

16. Promise Object (Introduction to ES6-Ruan Yifeng)

The meaning of Promise


Promise is a solution for asynchronous programming, which is more reasonable and more powerful than traditional solutions callback functions and events. It was first proposed and implemented by the community. ES6 wrote it into the language standard, unified usage, and provided natively

Promise
Object.

So-called

Promise
, Simply put, it is a container that holds the result of an event (usually an asynchronous operation) that will end in the future. Syntactically speaking, a Promise is an object, from which you can obtain messages for asynchronous operations. Promise provides a unified API, and various asynchronous operations can be processed in the same way.

Promise
The object has the following two characteristics.

(1) The state of the object is not affected by the outside world.

Promise
The object represents an asynchronous operation and has three states:
pending
(processing),
fulfilled
(Successful) and
rejected
(Failed). Only the result of the asynchronous operation can determine the current state, and no other operation can change this state. This is also
Promise
The origin of this name, its English meaning is "promise", which means that other means cannot be changed.

(2) Once the state changes, it will not change again, and this result can be obtained at any time.

Promise
There are only two possibilities for the state of the object to change: from
pending
Becomes
fulfilled
And from
pending
Becomes
rejected
. As long as these two situations occur, the state will be frozen and will not change anymore. This result will always be maintained. At this time, it is called resolved (finalized). If the change has already happened, you are right
Promise
Adding a callback function to the object will also get this result immediately. This is completely different from an event. The feature of an event is that if you miss it, you can listen to it and you will get no results.

Note that for the convenience of writing, the

resolved
Unity refers only to
fulfilled
Status, does not contain
rejected
status.

With

Promise
Object, the asynchronous operation can be expressed in the process of synchronous operation, avoiding the nested callback function. In addition,
Promise
Objects provide a unified interface, making it easier to control asynchronous operations.

Promise
There are also some disadvantages. First of all, it cannot be cancelled
Promise
, Once it is created, it will be executed immediately and cannot be canceled midway. 2. if you don t set a callback function,
Promise
Errors thrown internally will not be reflected to the outside. 3. when in
pending
In the state, it is impossible to know which stage the current progress is (just started or is about to complete).

If certain events happen repeatedly, generally speaking, using Stream mode is better than deploying

Promise
A better choice.

Basic usage

ES6 regulations,

Promise
The object is a constructor, used to generate
Promise
Instance.

The following code creates a

Promise
Instance.

const promise = new Promise(function(resolve, reject) { //... some code if (/* Asynchronous operation succeeded*/){ resolve(value); } else { reject(error); } }); Copy code

Promise
The constructor accepts a function as a parameter, and the two parameters of the function are
resolve
with
reject
. They are two functions, provided by the JavaScript engine, so you don't need to deploy them yourself.

resolve
The function of the function is to
Promise
The status of the object changes from "unfinished" to "successful" (that is, from pending to resolved), which is called when the asynchronous operation succeeds, and the result of the asynchronous operation is passed as a parameter;
reject
The function of the function is to
Promise
The status of the object changes from "incomplete" to "failed" (that is, from pending to rejected), which is called when the asynchronous operation fails, and the error reported by the asynchronous operation is passed as a parameter.

Promise
After the instance is generated, you can use
then
Methods are specified separately
resolved
Status and
rejected
The callback function of the state.

promise.then(function(value) { //success }, function(error) { //failure }); Copy code

then
The method can accept two callback functions as parameters. The first callback function is
Promise
The state of the object becomes
resolved
When called, the second callback function is
Promise
The state of the object becomes
rejected
When called. Both of these functions are optional and do not have to be provided. They all accept
Promise
The value passed by the object is used as a parameter.

Below is a

Promise
Simple examples of objects.

function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms,'done'); }); } timeout(100).then((value) => { console.log(value); }); Copy code

In the above code,

timeout
Method returns a
Promise
An instance, which means a result that will only occur after a period of time. The specified time has passed (
ms
Parameters) later,
Promise
The status of the instance becomes
resolved
, It will trigger
then
The callback function bound to the method.

Once the Promise is created, it will be executed immediately.

let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!'); //Promise //Hi! //resolved Copy code

In the above code, the Promise is executed immediately after it is created, so the first output is

Promise
. then,
then
The callback function specified by the method will be executed after all the synchronization tasks of the current script are executed, so
resolved
Finally output.

The following is an example of asynchronous loading of pictures.

function loadImageAsync(url) { return new Promise(function(resolve, reject) { const image = new Image(); image.onload = function() { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at '+ url)); }; image.src = url; }); } Copy code

In the above code, use

Promise
Wraps an asynchronous operation of image loading. If the load is successful, call
resolve
Method, otherwise call
reject
method.

Here is a

Promise
Examples of Ajax operations implemented by objects.

const getJSON = function(url) { const promise = new Promise(function(resolve, reject){ const handler = function() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; const client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: '+ json); }, function(error) { console.error('Something went wrong', error); }); Copy code

In the above code,

getJSON
Is an encapsulation of the XMLHttpRequest object, used to issue an HTTP request for JSON data, and return a
Promise
Object. It s important to note that in
getJSON
internal,
resolve
Function and
reject
When a function is called, it always takes parameters.

If you call

resolve
Function and
reject
Functions with parameters, then their parameters will be passed to the callback function.
reject
The parameters of the function are usually
Error
An instance of the object, representing the error thrown;
resolve
In addition to the normal value, the parameter of the function may also be another Promise instance, such as the following.

const p1 = new Promise(function (resolve, reject) { //... }); const p2 = new Promise(function (resolve, reject) { //... resolve(p1); }) Copy code

In the above code,

p1
with
p2
Are instances of Promise, but
p2
of
resolve
Method will
p1
As a parameter, the result of an asynchronous operation is to return another asynchronous operation.

Note that at this time

p1
Will be passed to
p2
, That is,
p1
The status of
p2
status. in case
p1
The status is
pending
, Then
p2
The callback function will wait
p1
The status changes; if
p1
Is already
resolved
or
rejected
, Then
p2
The callback function will be executed immediately.

const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) //Error: fail Copy code

In the above code,

p1
Is a Promise, after 3 seconds it becomes
rejected
.
p2
The status of changes after 1 second,
resolve
What the method returns is
p1
. due to
p2
What is returned is another Promise, resulting in
p2
Own status is invalid, by
p1
The status of
p2
status. So, the back
then
The sentences are all turned against the latter (
p1
). After another 2 seconds,
p1
Becomes
rejected
, Leading to trigger
catch
The callback function specified by the method.

Note, call

resolve
or
reject
It does not end the execution of the Promise parameter function.

new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); //2 //1 Copy code

In the above code, call

resolve(1)
Later
console.log(2)
It will still be executed and will be printed out first. This is because the immediately resolved Promise is executed at the end of the current round of the event loop, which is always later than the synchronization task of the current round of the loop.

Generally speaking, call

resolve
or
reject
In the future, the mission of Promise will be completed, and subsequent operations should be placed in
then
Method, and should not be written directly in
resolve
or
reject
Behind. So, it s best to add
return
Statement, so there will be no surprises.

new Promise((resolve, reject) => { return resolve(1); //The following statement will not be executed console.log(2); }) Copy code

Promise.prototype.then()

Promise instance has

then
Method, that is,
then
Method is defined in the prototype object
Promise.prototype
Up. Its role is to add a callback function when the state of the Promise instance changes. As I said before,
then
The first parameter of the method is
resolved
The callback function of the state, the second parameter is
rejected
The callback function of the state, they are all optional.

then
The method returns a new
Promise
Instance (note, not the original one
Promise
Examples). So you can use chain writing, that is
then
Call another after the method
then
method.

getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { //... }); Copy code

The above code uses

then
Method, two callback functions are specified in turn. After the first callback function is completed, the return result will be used as a parameter and passed into the second callback function.

Chained

then
, You can specify a set of callback functions to be called in order. At this time, the previous callback function may return a
Promise
Object (that is, there is asynchronous operation), then the latter callback function will wait for the
Promise
Only when the state of the object changes, it will be called.

getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function (comments) { console.log("resolved: ", comments); }, function (err){ console.log("rejected: ", err); }); Copy code

In the above code, the first

then
The callback function specified by the method returns another
Promise
Object. At this time, the second
then
The callback function specified by the method will wait for this new
Promise
The status of the object changes. If it becomes
resolved
, Call the first callback function, if the status becomes
rejected
, Call the second callback function.

If you use arrow functions, the above code can be written more concisely.

getJSON("/post/1.json").then( post => getJSON(post.commentURL) ).then( comments => console.log("resolved: ", comments), err => console.log("rejected: ", err) ); Copy code

Promise.prototype.catch()

Promise.prototype.catch()
the way is
.then(null, rejection)
or
.then(undefined, rejection)
The alias of is used to specify the callback function when an error occurs.

getJSON('/posts.json').then(function(posts) { //... }).catch(function(error) { //Handle errors that occur when getJSON and the previous callback function are running console.log('An error occurred!', error); }); Copy code

In the above code,

getJSON()
Method returns a Promise object, if the object state becomes
resolved
, It will call
then()
The callback function specified by the method; if the asynchronous operation throws an error, the status will change to
rejected
, It will call
catch()
The callback function specified by the method handles this error. In addition,
then()
The callback function specified by the method, if an error is thrown during operation, will also be
catch()
Method capture.

p.then((val) => console.log('fulfilled:', val)) .catch((err) => console.log('rejected', err)); //Equivalent to p.then((val) => console.log('fulfilled:', val)) .then(null, (err) => console.log("rejected:", err)); Copy code

Below is an example.

const promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); //Error: test Copy code

In the above code,

promise
Throw an error and get
catch()
The callback function specified by the method captures. Note that the above writing is equivalent to the following two writings.

//Writing method one const promise = new Promise(function(resolve, reject) { try { throw new Error('test'); } catch(e) { reject(e); } }); promise.catch(function(error) { console.log(error); }); //Writing method two const promise = new Promise(function(resolve, reject) { reject(new Error('test')); }); promise.catch(function(error) { console.log(error); }); Copy code

Comparing the above two ways of writing, you can find

reject()
The function of the method is equivalent to throwing an error.

If the Promise state has become

resolved
, Throwing an error again is invalid.

const promise = new Promise(function(resolve, reject) { resolve('ok'); throw new Error('test'); }); promise .then(function(value) {console.log(value) }) .catch(function(error) {console.log(error) }); //ok Copy code

In the above code, the Promise is

resolve
After the statement, if an error is thrown again, it will not be caught, which is equivalent to not thrown. Because once the state of the Promise changes, it will remain in that state forever and won't change anymore.

The error of the Promise object has a "bubble" nature and will be passed backwards until it is caught. In other words, the error will always be the next

catch
Statement capture.

getJSON('/post/1.json').then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { //some code }).catch(function(error) { //Handle the errors generated by the first three Promises }); Copy code

In the above code, there are three Promise objects: one is

getJSON()
Produced by
then()
produce. The error thrown by any of them will be the last one
catch()
capture.

Generally speaking, don t

then()
The method defines the callback function of the Reject state (ie
then
The second parameter), always use
catch
method.

//bad promise .then(function(data) { //success }, function(err) { //error }); //good promise .then(function(data) {//cb //success }) .catch(function(err) { //error }); Copy code

In the above code, the second way of writing is better than the first way of writing, because the second way of writing can capture the previous

then
Errors in method execution are also closer to synchronous writing (
try/catch
). Therefore, it is recommended to always use
catch()
Method instead of using
then()
The second parameter of the method.

With the traditional

try/catch
The difference between the code block is that if it is not used
catch()
The method specifies the callback function for error handling, and the error thrown by the Promise object will not be passed to the outer code, that is, there will be no response.

const someAsyncThing = function() { return new Promise(function(resolve, reject) { //The next line will report an error because x is not declared resolve(x + 2); }); }; someAsyncThing().then(function() { console.log('everything is great'); }); setTimeout(() => {console.log(123) }, 2000); //Uncaught (in promise) ReferenceError: x is not defined //123 Copy code

In the above code,

someAsyncThing()
The Promise object generated by the function has a syntax error inside. The browser runs to this line, it will print out an error message
ReferenceError: x is not defined
, But will not exit the process, terminate the script execution, and will still output after 2 seconds
123
. This means that errors inside the Promise will not affect the code outside the Promise. The popular saying is "Promise will eat the error".

This script is executed on the server, and the exit code is

0
(That means the execution was successful). However, Node.js has a
unhandledRejection
Events, specifically listening for uncaught
reject
Error, the above script will trigger the listener function for this event, and errors can be thrown in the listener function.

process.on('unhandledRejection', function (err, p) { throw err; }); Copy code

In the above code,

unhandledRejection
The event listener function has two parameters. The first is the error object, and the second is the Promise instance that reported the error. It can be used to understand the environment in which the error occurred.

Note that Node has plans to abolish in the future

unhandledRejection
event. If there is an uncaught error inside the Promise, the process will be terminated directly, and the exit code of the process is not 0.

Look at the following example again.

const promise = new Promise(function (resolve, reject) { resolve('ok'); setTimeout(function () {throw new Error('test') }, 0) }); promise.then(function (value) {console.log(value) }); //ok //Uncaught Error: test Copy code

In the above code, Promise specifies that an error will be thrown in the next round of the "event loop". At that time, the operation of the Promise has ended, so this error is thrown outside the Promise function, will bubble up to the outermost layer, and become an uncaught error.

It is always recommended that the Promise object should be followed by

catch()
Method, which can handle errors that occur inside the Promise.
catch()
The method returns a Promise object, so you can call it later
then()
method.

const someAsyncThing = function() { return new Promise(function(resolve, reject) { //The next line will report an error because x is not declared resolve(x + 2); }); }; someAsyncThing() .catch(function(error) { console.log('oh no', error); }) .then(function() { console.log('carry on'); }); //oh no [ReferenceError: x is not defined] //carry on Copy code

After the above code runs

catch()
The callback function specified by the method will then run the latter
then()
The callback function specified by the method. If no error is reported, it will be skipped
catch()
method.

Promise.resolve() .catch(function(error) { console.log('oh no', error); }) .then(function() { console.log('carry on'); }); //carry on Copy code

The above code is skipped because there is no error

catch()
Method, directly execute the following
then()
method. At this point, if
then()
The error in the method is the same as the previous
catch()
It doesn't matter anymore.

catch()
In the method, errors can be thrown again.

const someAsyncThing = function() { return new Promise(function(resolve, reject) { //The next line will report an error because x is not declared resolve(x + 2); }); };21 someAsyncThing().then(function() { return someOtherAsyncThing(); }).catch(function(error) { console.log('oh no', error); //The next line will report an error because y is not declared y + 2; }).then(function() { console.log('carry on'); }); //oh no [ReferenceError: x is not defined] Copy code

In the above code,

catch()
Method throws an error because there is nothing else behind
catch()
Method, the error will not be caught, nor will it be passed to the outer layer. If you rewrite it, the result will be different.

someAsyncThing().then(function() { return someOtherAsyncThing(); }).catch(function(error) { console.log('oh no', error); //The next line will report an error because y is not declared y + 2; }).catch(function(error) { console.log('carry on', error); }); //oh no [ReferenceError: x is not defined] //carry on [ReferenceError: y is not defined] Copy code

In the above code, the second

catch()
Method is used to capture the previous
catch()
The error thrown by the method.

Promise.prototype.finally()

finally()
Methods are used to specify the operations that will be performed regardless of the final state of the Promise object. This method is the standard introduced by ES2018.

promise .then(result => { }) .catch(error => { }) .finally(() => { }); Copy code

In the above code, no matter

promise
The final state, after the execution
then
or
catch
After the specified callback function, it will be executed
finally
The callback function specified by the method.

The following is an example, the server uses Promise to process the request, and then uses

finally
Method to turn off the server.

server.listen(port) .then(function () { //... }) .finally(server.stop); Copy code

finally
The callback function of the method does not accept any parameters, which means that there is no way to know whether the previous Promise state is
fulfilled
still is
rejected
. This indicates,
finally
The operation in the method should be independent of the state and not dependent on the execution result of the Promise.

finally
Essentially
then
A special case of the method.

promise .finally(() => { //statement }); //Equivalent to promise .then( result => { //statement return result; }, error => { //statement throw error; } ); Copy code

In the above code, if you don t use

finally
Method, the same statement needs to be written once for both success and failure. With
finally
Method, you only need to write it once.

Its implementation is also very simple.

Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => {throw reason }) ); }; Copy code

In the above code, regardless of the previous Promise is

fulfilled
still is
rejected
, Will execute the callback function
callback
.

As can be seen from the above implementation,

finally
The method always returns the original value.

//The value of resolve is undefined Promise.resolve(2).then(() => {}, () => {}) //The value of resolve is 2 Promise.resolve(2).finally(() => {}) //The value of reject is undefined Promise.reject(3).then(() => {}, () => {}) //The value of reject is 3 Promise.reject(3).finally(() => {}) Copy code

Promise.all()

Promise.all()
The method is used to wrap multiple Promise instances into a new Promise instance.

const p = Promise.all ([p1, p2, p3]); Copy Code

In the above code,

Promise.all()
The method accepts an array as a parameter,
p1
,
p2
,
p3
They are all Promise instances, if they are not, the following will be called first
Promise.resolve
Method, the parameters are converted to Promise instances, and then processed further. In addition,
Promise.all()
The method parameter may not be an array, but it must have an Iterator interface, and each member returned is a Promise instance.

p
'S status is determined by
p1
,
p2
,
p3
The decision is divided into two situations.

(1) Only

p1
,
p2
,
p3
Status becomes
fulfilled
,
p
Will become
fulfilled
,at this time
p1
,
p2
,
p3
The return value is formed into an array and passed to
p
Callback function.

(2) As long as

p1
,
p2
,
p3
One of them is
rejected
,
p
The status becomes
rejected
, At this time the first one was
reject
The return value of the instance will be passed to
p
Callback function.

The following is a specific example.

//Generate an array of Promise objects const promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON('/post/' + id + ".json"); }); Promise.all(promises).then(function (posts) { //... }).catch(function(reason){ //... }); Copy code

In the above code,

promises
Is an array containing 6 Promise instances, only the state of these 6 instances becomes
fulfilled
, Or one of them becomes
rejected
Before calling
Promise.all
The callback function after the method.

Here is another example.

const databasePromise = connectDatabase(); const booksPromise = databasePromise .then(findAllBooks); const userPromise = databasePromise .then(getCurrentUser); Promise.all([ booksPromise, userPromise ]) .then(([books, user]) => pickTopRecommendations(books, user)); Copy code

In the above code,

booksPromise
with
userPromise
Are two asynchronous operations, only when their results are returned, will they be triggered
pickTopRecommendations
This callback function.

Note that if the Promise instance as a parameter is defined by yourself

catch
Method, then once it is
rejected
, Does not trigger
Promise.all()
of
catch
method.

const p1 = new Promise((resolve, reject) => { resolve('hello'); }) .then(result => result) .catch(e => e); const p2 = new Promise((resolve, reject) => { throw new Error('Reported an error'); }) .then(result => result) .catch(e => e); Promise.all([p1, p2]) .then(result => console.log(result)) .catch(e => console.log(e)); //["hello", Error: reported an error] Copy code

In the above code,

p1
meeting
resolved
,
p2
First will
rejected
,but
p2
Have one's own
catch
Method, this method returns a new Promise instance,
p2
The point is actually this instance. The instance is executed
catch
After the method, it will also become
resolved
,resulting in
Promise.all()
Both instances in the method parameters will
resolved
, So it will call
then
The callback function specified by the method will not be called
catch
The callback function specified by the method.

in case

p2
No own
catch
Method, it will call
Promise.all()
of
catch
method.

const p1 = new Promise((resolve, reject) => { resolve('hello'); }) .then(result => result); const p2 = new Promise((resolve, reject) => { throw new Error('Reported an error'); }) .then(result => result); Promise.all([p1, p2]) .then(result => console.log(result)) .catch(e => console.log(e)); //Error: reported an error Copy code

Promise.race()

Promise.race()
The method is also to wrap multiple Promise instances into a new Promise instance.

const p = Promise.race ([p1, p2, p3]); Copy Code

In the above code, as long as

p1
,
p2
,
p3
There is an instance that changes the state first,
p
The status changes accordingly. The return value of the Promise instance that changed first is passed to
p
Callback function.

Promise.race()
The parameters of the method and
Promise.all()
The method is the same, if it is not a Promise instance, the following will be called first
Promise.resolve()
Method, the parameters are converted to Promise instances, and then processed further.

The following is an example, if no result is obtained within the specified time, the state of the Promise is

reject
, Otherwise it becomes
resolve
.

const p = Promise.race([ fetch('/resource-that-may-take-a-while'), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); p .then(console.log) .catch(console.error); Copy code

In the above code, if within 5 seconds

fetch
The method cannot return the result, the variable
p
Will become
rejected
To trigger
catch
The callback function specified by the method.

Promise.allSettled()

Promise.allSettled()
The method accepts a set of Promise instances as parameters and wraps them into a new Promise instance. Only wait until all these parameter instances return results, whether it is
fulfilled
still is
rejected
, The packaging instance will end. This method was introduced by ES2020 .

const promises = [ fetch('/api-1'), fetch('/api-2'), fetch('/api-3'), ]; await Promise.allSettled(promises); removeLoadingIndicator(); Copy code

The above code sends three requests to the server and waits until all three requests are over. Regardless of whether the request succeeds or fails, the loaded scroll icon will disappear.

The new Promise instance returned by this method, once it ends, the state is always

fulfilled
, Will not become
rejected
. Status becomes
fulfilled
Later, the parameter received by the Promise listener function is an array, and each member corresponds to an incoming
Promise.allSettled()
The Promise instance.

const resolved = Promise.resolve(42); const rejected = Promise.reject(-1); const allSettledPromise = Promise.allSettled([resolved, rejected]); allSettledPromise.then(function (results) { console.log(results); }); //[ //{status:'fulfilled', value: 42 }, //{status:'rejected', reason: -1} //] Copy code

In the above code,

Promise.allSettled()
The return value
allSettledPromise
, The status can only become
fulfilled
. The parameter received by its listening function is an array
results
. Each member of the array is an object, corresponding to the incoming
Promise.allSettled()
The two Promise instances. Every object has
status
Attribute, the value of the attribute can only be a string
fulfilled
Or string
rejected
.
fulfilled
When the object has
value
Attributes,
rejected
Sometimes
reason
Attribute, corresponding to the return value of the two states.

The following is an example of return value usage.

const promises = [fetch('index.html'), fetch('https://does-not-exist/') ]; const results = await Promise.allSettled(promises); //Filter out successful requests const successfulPromises = results.filter(p => p.status ==='fulfilled'); //Filter out failed requests and output the reason const errors = results .filter(p => p.status ==='rejected') .map(p => p.reason); Copy code

Sometimes, we don't care about the results of asynchronous operations, only whether these operations are over. At this time,

Promise.allSettled()
The method is very useful. Without this method, it would be troublesome to ensure that all operations are completed.
Promise.all()
The method cannot do this.

const urls = [/* ... */]; const requests = urls.map(x => fetch(x)); try { await Promise.all(requests); console.log('All requests are successful.'); } catch { console.log('At least one request failed, other requests may not be over yet.'); } Copy code

In the above code,

Promise.all()
Cannot be sure that all requests are over. To achieve this goal, it is very troublesome to write, with
Promise.allSettled()
, It's easy.

Promise.any()

ES2021 introduced

Method . This method accepts a set of Promise instances as parameters and wraps them into a new Promise instance to return. As long as one of the parameter instances becomes
fulfilled
State, the packaging instance will become
fulfilled
Status; if all parameter instances become
rejected
State, the packaging instance will become
rejected
status.

Promise.any()
with
Promise.race()
The method is very similar, with only one difference, that is, it will not become
rejected
State and end.

const promises = [ fetch('/endpoint-a').then(() =>'a'), fetch('/endpoint-b').then(() =>'b'), fetch('/endpoint-c').then(() =>'c'), ]; try { const first = await Promise.any(promises); console.log(first); } catch (error) { console.log(error); } Copy code

In the above code,

Promise.any()
The parameter array of the method contains three Promise operations. As long as one of them becomes
fulfilled
,
Promise.any()
The returned Promise object becomes
fulfilled
. If all three operations become
rejected
, Then
await
The command will throw an error.

Promise.any()
The error thrown is not a general error, but an instance of AggregateError. It is equivalent to an array, each member corresponds to a
rejected
The error thrown by the operation. The following is an implementation example of AggregateError.

new AggregateError() extends Array -> AggregateError const err = new AggregateError(); err.push(new Error("first error")); err.push(new Error("second error")); throw err; Copy code

When catching errors, if you don t use

try...catch
The structure and await command can be written as follows.

Promise.any(promises).then( (first) => { //Any of the promises was fulfilled. }, (error) => { //All of the promises were rejected. } ); Copy code

Below is an example.

var resolved = Promise.resolve(42); var rejected = Promise.reject(-1); var alsoRejected = Promise.reject(Infinity); Promise.any([resolved, rejected, alsoRejected]).then(function (result) { console.log(result);//42 }); Promise.any([rejected, alsoRejected]).catch(function (results) { console.log(results);//[-1, Infinity] }); Copy code

Promise.resolve()

Sometimes it is necessary to convert an existing object into a Promise object,

Promise.resolve()
The method plays this role.

const jsPromise = Promise.resolve ($ ajax ( '/whatever.json').); duplicated code

The above code will be generated by jQuery

deferred
The object is transformed into a new Promise object.

Promise.resolve()
It is equivalent to the following wording.

Promise.resolve('foo') //Equivalent to new Promise(resolve => resolve('foo')) Copy code

Promise.resolve()
The parameters of the method are divided into four cases.

(1) The parameter is a Promise instance

If the parameter is a Promise instance, then

Promise.resolve
Will return to this instance without any modification.

(2) The parameter is a

thenable
Object

thenable
Object refers to having
then
The object of the method, such as the following object.

let thenable = { then: function(resolve, reject) { resolve(42); } }; Copy code

Promise.resolve()
The method will turn this object into a Promise object, and then execute it immediately
thenable
Object of
then()
method.

let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function (value) { console.log(value);//42 }); Copy code

In the above code,

thenable
Object of
then()
After the method is executed, the object
p1
The status becomes
resolved
To execute the last one immediately
then()
The callback function specified by the method, output 42.

(3) The parameter does not have

then()
The object of the method, or not the object at all

If the parameter is a primitive value, or one that does not have

then()
The object of the method, then
Promise.resolve()
The method returns a new Promise object with the status
resolved
.

const p = Promise.resolve('Hello'); p.then(function (s) { console.log(s) }); //Hello Copy code

The above code generates an instance of a new Promise object

p
. Due to the string
Hello
It is not an asynchronous operation (the judgment method is that the string object does not have the then method), and the state of the returned Promise instance is from the moment it is generated
resolved
, So the callback function will be executed immediately.
Promise.resolve()
The parameters of the method will also be passed to the callback function.

(4) Without any parameters

Promise.resolve()
The method allows to call without parameters, directly return a
resolved
The state's Promise object.

So, if you want to get a Promise object, the more convenient way is to call it directly

Promise.resolve()
method.

const p = Promise.resolve(); p.then(function () { //... }); Copy code

Variables of the above code

p
It is a Promise object.

It s important to note that immediately

resolve()
The Promise object is executed at the end of the current event loop , not at the beginning of the next event loop .

setTimeout(function () { console.log('three'); }, 0); Promise.resolve().then(function () { console.log('two'); }); console.log('one'); //one //two //three Copy code

In the above code,

setTimeout(fn, 0)
Executed at the beginning of the next round of "event loop",
Promise.resolve()
Executed at the end of this "event loop",
console.log('one')
It is executed immediately, so it is output first.

Promise.reject()

Promise.reject(reason)
The method will also return a new Promise instance whose status is
rejected
.

const p = Promise.reject('Something went wrong'); //Equivalent to const p = new Promise((resolve, reject) => reject('Something went wrong')) p.then(null, function (s) { console.log(s) }); //error Copy code

The above code generates an instance of a Promise object

p
, The status is
rejected
, The callback function will be executed immediately.

Promise.reject()
The parameters of the method will be used intact as
reject
The reason becomes the parameter of the subsequent method.

Promise.reject('Something went wrong') .catch(e => { console.log(e ==='Something went wrong') }) //true Copy code

In the above code,

Promise.reject()
The parameter of the method is a string, followed by
catch()
Method parameters
e
This is the string.

application

Load picture

We can write the loading of the picture as a

Promise
, Once the loading is complete,
Promise
The status changes.

const preloadImage = function (path) { return new Promise(function (resolve, reject) { const image = new Image(); image.onload = resolve; image.onerror = reject; image.src = path; }); }; Copy code

Combination of Generator function and Promise

Use the Generator function to manage the process. When it encounters an asynchronous operation, it usually returns a

Promise
Object.

function getFoo () { return new Promise(function (resolve, reject){ resolve('foo'); }); } const g = function* () { try { const foo = yield getFoo(); console.log(foo); } catch (e) { console.log(e); } }; function run (generator) { const it = generator(); function go(result) { if (result.done) return result.value; return result.value.then(function (value) { return go(it.next(value)); }, function (error) { return go(it.throw(error)); }); } go(it.next()); } run(g); Copy code

Generator function of the above code

g
Among them, there is an asynchronous operation
getFoo
, It returns a
Promise
Object. function
run
Used to handle this
Promise
Object and call the next
next
method.

Promise.try()

In actual development, we often encounter a situation: I don t know or don t want to distinguish, the function

f
Is it a synchronous function or an asynchronous operation, but I want to use Promise to handle it. Because then you can leave it alone
f
Whether to include asynchronous operations, use
then
Method to specify the next process, use
catch
Method processing
f
The error thrown. Generally, the following wording will be used.

Promise.resolve().then(f) Copy code

The above writing has a disadvantage, that is, if

f
Is a synchronous function, then it will be executed at the end of this round of event loop.

const f = () => console.log('now'); Promise.resolve().then(f); console.log('next'); //next //now Copy code

In the above code, the function

f
It is synchronous, but after wrapping with Promise, it becomes asynchronous execution.

So is there a way to make synchronous functions execute synchronously and asynchronous functions execute asynchronously, and let them have a unified API? The answer is yes, and there are two ways to write it. The first way of writing is to use

async
Function to write.

const f = () => console.log('now'); (async () => f())(); console.log('next'); //now //next Copy code

In the above code, the second line is an anonymous function that is executed immediately, which will be executed immediately

async
Function, so if
f
Is synchronized, you will get the result of synchronization; if
f
Is asynchronous, you can use
then
Specify the next step, as shown below.

(async () => f())() .then(...) Copy code

have to be aware of is,

async () => f()
Will eat
f()
The error thrown. So, if you want to catch errors, use
promise.catch
method.

(async () => f())() .then(...) .catch(...) Copy code

The second way of writing is to use

new Promise()
.

const f = () => console.log('now'); ( () => new Promise( resolve => resolve(f()) ) )(); console.log('next'); //now //next Copy code

The above code also uses an anonymous function that executes immediately, execute

new Promise()
. In this case, the synchronization function is also executed synchronously.

Given that this is a very common requirement, there is now a proposal to provide

Promise.try
Method replaces the above wording.

const f = () => console.log('now'); Promise.try(f); console.log('next'); //now //next Copy code

In fact,

Promise.try
Long-lived, Promise library
Bluebird
,
Q
with
when
, This method has long been provided.

due to

Promise.try
Provides a unified processing mechanism for all operations, so if you want to use
then
Method management process, it is best to use
Promise.try
Pack it up. This has many advantages , one of which is that it can better manage exceptions.

function getUsername(userId) { return database.users.get({id: userId}) .then(function(user) { return user.name; }); } Copy code

In the above code,

database.users.get()
Return a Promise object, if an asynchronous error is thrown, you can use
catch
Method capture, as written below.

database.users.get({id: userId}) .then(...) .catch(...) Copy code

but

database.users.get()
May also throw synchronization errors (such as database connection errors, depending on the implementation method), then you have to use
try...catch
To capture.

try { database.users.get({id: userId}) .then(...) .catch(...) } catch (e) { //... } Copy code

The above writing method is very clumsy, then you can use it uniformly

promise.catch()
Catch all synchronous and asynchronous errors.

Promise.try(() => database.users.get({id: userId})) .then(...) .catch(...) Copy code

In fact,

Promise.try
Is simulation
try
Code block, like
promise.catch
The simulation is
catch
Code block.