Reasons for writing this article:
- Asynchronous is a high frequency application scenario and should be everywhere
- Usually accumulated knowledge is scattered, not formed a complete system, so thematic and put into practice, to master ambiguous knowledge
- Establish the relationship between mind, body and emotion
- Recharge your batteries every day and your quality of life changes
See the receipt of this article:
- Understand Promise and await/async information that is not easy to notice
- Frequently used usage scenarios
For further information, see my other Promise and await async summary
I think the knowledge points of these two articles are a little repetitive, because there must be intersection while summarizing and thinking, so I think the quality of the article: there is no best, only better.
There are new knowledge points to continue to fill in the back, also welcome to correct.
Here say next in the heart a little idea: output high attraction article, hope benefit also oneself, hope everybody gives 👍, do not spend money!
A Promise,
Before writing the text, take an example of an interface that can be requested from the Internet
https://api.github.com/users/github function get(url, params, config = {}) { return new Promise((resolve, reject) => { axios.get(url, { params, ... config, }).then(res => { resolve(res.data); }).catch(err => { reject(err.data); })}); } The value obtained after the request is replaced by [" real requested value "]Copy the code
1. Basic usage
1.1 P1 is in p2 state
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 the code
P1 is a Promise, which becomes rejected after 3 seconds. The status of P2 changes after 1 second, and the resolve method returns P1. Since P2 returned another Promise, the status of P2 is invalid. The status of P2 is determined by the status of P1. Therefore, all subsequent THEN statements become for the latter (P1). After another 2 seconds p1 becomes rejected, causing the callback function specified by the catch method to be fired.
The function after 1.2 resolve also executes
new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { console.log(r); }); / / / 1/2Copy the code
1.3 Resolve and return are not executed again
new Promise((resolve, reject) => { return resolve(1); // The following statement will not execute console.log(2); })Copy the code
In general, after a call to resolve or reject, the Promise’s mission is complete, and subsequent operations should be placed inside the THEN method, rather than written directly after resolve or reject. Therefore, it is best to precede them with a return statement so that there are no surprises.
The THEN method returns a new Promise instance, so you can use the chained method. After the first callback completes, it passes the result as an argument to the second callback.
Return the value of the promise
Get (' https://api.github.com/users/github '). Then (res = > {the console. The log (' first: 'res) / / "first: real request to the value of the return get('https://api.github.com/users/github'); }). Then ((res) = > {the console. The log (' second: 'res) / / "second: request to the real value"})Copy the code
Returns a value directly
First: / / "request to the real value of" get (' https://api.github.com/users/github '). Then (res = > {the console. The log (' first: 'res) / / "first: request to the real value" return 123; }).then((res)=>{ console.log('second:',res) //"second:123" })Copy the code
2. Promise.prototype.then()
The THEN method is defined on the promise.prototype object. The function is to add a callback to a Promise instance when its state changes.
3. Promise.prototype.catch()
3.1 Promise.prototype.catch() is an alias for.then(null, rejection) or.then(undefined, rejection), which specifies the callback function when an error occurs.
get('https://api.github.com/users/github').then(res => {
throw new Error('error');
}).catch((res)=>{
console.log('second:',res) //second: Error: error
})
get('https://api.github.com/users/github').then(res => {
throw new Error('error');
}).then(null,err => {
console.log('err:',err) //err: Error: error
})
Copy the code
The following two writing methods are equivalent
Const promise = new promise (function(resolve, reject) {try {throw new Error('test'); } catch(e) {reject(e); }}); promise.catch(function(error) { console.log(error); }); Const promise = new Promise(function(resolve, reject) {reject(new Error('test')); // reject}); promise.catch(function(error) { console.log(error); });Copy the code
3.2 If the Promise state has changed to Resolved, throwing an error is invalid
const promise = new Promise(function(resolve, reject) { resolve('ok'); throw new Error('test'); // invalid}); promise .then(function(value) { console.log(value) }) .catch(function(error) { console.log(error) }); // okCopy the code
3.3 The errors of a Promise object “bubble” and are passed back until they are caught. That is, the error is always caught by the next catch statement
Get (' https://api.github.com/users/github '). Then (res = > {the console. The log (' first: 'res) / / "first: real request to the value of the return get('https://api.github.com/users/github'); }). Then ((res) = > {the console. The log (' second: 'res) / / "second: request to the real value"})Copy the code
In the above code, there are three Promise objects: one generated by get() and two generated by THEN (). Any error thrown by either of them will be caught by the last catch().
3.4 It is best not to define the Reject state callback in the THEN () method. Always use the catch method.
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
Copy the code
The reason for this is that the second method can catch errors in the previous then method execution and is much closer to the synchronous (try/catch) method.
3.5 An error inside a Promise does not affect code executives outside of the Promise
Popularly known as “promises eat mistakes.”
Const someAsyncThing = function() {return new Promise(function(resolve, reject) {// The next line will fail because x does not declare resolve(x + 2); // This line will throw an error, but will not exit the process and terminate the script execution}); }; someAsyncThing().then(function() { console.log('everything is great'); }); setTimeout(() => { console.log(123) }, 2000); Undefined (in promise) ReferenceError: x is not defined // 123Copy the code
In this example, it is better to use catch
Unlike traditional try/catch blocks, if there is no callback function that uses the catch() method to specify error handling, the error thrown by a Promise object is not passed to the outer code, that is, there is no response.
3.6 Promise throws the error again in the next round of “event loop”, which can catch
const promise = new Promise(function (resolve, reject) { resolve('ok'); SetTimeout (function () {throw new Error('test')}, 0); setTimeout(function () {throw new Error('test')}, 0) promise.then(function (value) { console.log(value) }); // ok // Uncaught Error: testCopy the code
3.7 Throwing Errors in the Same Round of “Event Loop”
Page error, no catch, no output error message, because resolved, throwing error is invalid
const promise = new Promise(function (resolve, reject) {
resolve('ok');
throw new Error('test')
});
promise.then(function (value) { console.log(value) });
//ok
Copy the code
There’s no catch
const promise = new Promise(function (resolve, reject) {
resolve('ok');
throw new Error('test')
});
promise
.then( (value)=> { console.log(value) })
.catch(e => {console.log('err:',e)});
//ok
Copy the code
It can be captured without resolve
const promise = new Promise(function (resolve, reject) {
throw new Error('test')
});
promise
.then( (value)=> { console.log(value) })
.catch(e => {console.log('err:',e)});
//err: Error: test
Copy the code
3.8 It is always recommended that a Promise object be followed by a catch() method to handle errors that occur within the Promise
The catch() method still returns a Promise object, so the then() method can be called later
Const someAsyncThing = function() {return new Promise(function(resolve, reject) {// The next line will fail because x does not declare 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 onCopy the code
No errors, skip the catch and execute the then() method directly. If an error occurs in the then() method, the catch() is irrelevant
Promise.resolve()
.catch(function(error) {
console.log('oh no', error);
})
.then(function() {
console.log('carry on');
});
// carry on
Copy the code
3.9 Catch can also throw an error
Const someAsyncThing = function() {return new Promise(function(resolve, reject) {// The next line will fail because x does not declare resolve(x + 2); }); }; someAsyncThing().then(function() { return someOtherAsyncThing(); }).catch(function(error) { console.log('oh no', error); // The following line will report an error because y does not declare 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 the code
The second catch() method is used to catch the error thrown by the previous catch() method
4. Promise.prototype.finally()
The finally callback is executed after the then or catch callback, regardless of the final state of the promise
Promise.resolve(2).then(() => {}, Resolve (2). Finally (() => {}) // Resolve () => {}) // Reject () => {}, () = > {}) / / reject a value of 3 Promise. Reject (3) finally (() = > {})Copy the code
5. Promise.all()
const p = Promise.all([p1, p2, p3]);
5.1 Arguments may not be arrays, but they must have an Iterator interface and each returned member is a Promise instance
5.2 All the states in the parameter become fulfilled, p becomes fulfilled, and the returned values form an array
If one of the 5.3 arguments is rejected, p becomes rejected
5.4 If the parameter is not a Promise instance, the promise. resolve method is called to convert the parameter to a Promise instance
It is better to follow catch
If a Promise instance has its own catch method, it will not emit the catch method of Promise.all() once it is rejected.
const p1 = new Promise((resolve, reject) => { resolve('hello'); }) .then(result => result) .catch(e => e); Const p2 = new Promise((resolve, reject) => {throw new Error(' Error '); }) .then(result => result) .catch(e => e); Promise.all([p1, p2]) .then(result => console.log(result)) .catch(e => console.log(e)); // ["hello", Error: Error is reported]Copy the code
Only the first error will be reported
Const p1 = new Promise((resolve, reject) => {throw new Error(' Error 1'); }) .then(result => result); Const p2 = new Promise((resolve, reject) => {throw new Error(' Error 2'); }) .then(result => result); Promise.all([p1, p2]) .then(result => console.log(result)) .catch(e => console.log(e)); //Error: Error 1Copy the code
6. Promise.race()
const p = Promise.race([p1, p2, p3]);
6.1 Parameter Rules The same as promise.all ()
6.2 As long as one of the parameters changes state first, the state of p will change accordingly
6.3 Application Scenario: An Error Message is reported if no result is obtained within a specified period
const p = Promise.race([ get('https://api.github.com/users/github'), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); P. Then (console.log).catch(console.errorCopy the code
7. Promise.allSettled()
This will be fulfilled someday or never until all parameter instances have returned the result.
7.2 The result is an array and the elements are objects, as shown in the following code
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}, // [// {status: 'fulfilled', value: 42}, // {status: 'rejected', reason: -1}, //Copy the code
You can use the status
7.3 Application Scenario: You do not care about the result of an asynchronous operation. You only care about whether the operation is complete
8. Promise.any()
8.1 As long as one of the parameter instances becomes fulfilled, the packaging instance will become fulfilled
8.2 If all parameter instances are in the Rejected state, the wrapper instance will be in the Rejected state
8.3 Is similar to promise.race () except that it does not end when a Promise becomes rejected
Error catching can be used
9. Promise.resolve()
9.1 Can convert an existing Object to a Promise Object
Promise.resolve('foo') // Equivalent to new Promise(resolve => resolve('foo'))Copy the code
9.2 Parameters are classified into four types
(1) The argument is a Promise instance
Promise.resolve will return the instance unchanged, with no modifications.Copy the code
(2) The argument is a thenable object
let thenable = { then: function(resolve, reject) { resolve(42); }}; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); / / 42});Copy the code
After the THEN method on the thenable object is executed, the p1 object becomes in the Resolved state and immediately executes the callback function specified by the last THEN method, resulting in 42
(3) The argument is not an object with a THEN method, or not an object at all
Returns a new Promise object in the Resolved state.
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
Copy the code
(4) Without any parameters
const p = Promise.resolve();
p.then(function () {
// ...
});
Copy the code
10. Promise.reject()
10.1 Return a new Promise instance with the status rejected
Const p = promise.reject (' something went wrong '); Const p = new Promise((resolve, reject) => reject(' something went wrong ')) p.hen (null, function (s) {console.log(s)}); / / make a mistakeCopy the code
10.2 What arguments to pass and what arguments to use as a reject reason
Note: In contrast to the promise.resolve method, the arguments to the: promise.reject () method are left as the reason for the reject and become arguments to subsequent methods.
Const thenable = {then(resolve, reject) {reject(' something went wrong '); }}; Catch (e => {console.log(e === thenable)}) // Promise. Reject (thenable). Instead of taking the "something went wrong" string thrown by Reject, the catch method takes a Thenable object.Copy the code
Examples of 11.
- Image to load
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 the code
- An asynchronous request
function get(url, params, config = {}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params,
...config,
}).then(res => {
resolve(res.data);
}).catch(err => {
reject(err.data);
})
});
}
Copy the code
- Components that encapsulate front and back buttons, such as messageBox
Yes: Indicates that the promise is in the resolve state. Cancel: indicates that the promise is in the resolve state
buttonAction() {
this.resolve();
}
Copy the code
Second, the async/await
Characteristics of 1.
- Built-in actuator
- The async function returns a Promise object, which can be used as an argument to the await command
- Better semantics than asterisks and yields
- Await can be followed by Promise objects and primitive values (numeric, string, and Boolean values, but in this case automatically converted to an immediately resolved Promise object)
- Await must be written in the async function
- The value returned by the return statement inside the async function becomes an argument to the then method callback.
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
Copy the code
- Any Promise object that follows an await statement becomes reject, and the entire Async function is interrupted.
Async function f() {await promise.reject (' something went wrong '); await Promise.resolve('hello world'); // Will not execute}Copy the code
How can it not be interrupted?
Method 1: Place the first await in the try… Inside the catch structure
Async function f() {try {await promise.reject (' something went wrong '); } catch(e) { } return await Promise.resolve('hello world'); } f() .then(v => console.log(v)) // hello worldCopy the code
Method 2: The Promise object followed by the await is followed by a catch method to handle any errors that may have occurred earlier
Async function f() {await promise.reject (' something went wrong ').catch(e => console.log(e)); return await Promise.resolve('hello world'); } f().then(v => console.log(v)Copy the code
2. Function writing
Const foo = async function () {} const foo = async function () {}; // Object method let obj = {async foo() {}}; obj.foo().then(...) // Class Storage {constructor() {this.cachePromise = cacheaches. Open ('avatars'); } async getAvatar(name) { const cache1 = await this.cachePromise; return name; // Return the value as an argument in then}} const storage = new storage (); Storage. GetAvatar (' jake). Then (...). ; Const foo = async () => {};Copy the code
3. Arguments after await
(1) If it is not a Promise object, return the corresponding value
Async function f() {// equal // return 123; return await 123; } f().then(v => console.log(v)) // 123Copy the code
(2) The await command is followed by a thenable object, equating it to a Promise object
class Sleep { constructor(timeout) { this.timeout = timeout; } then(reject, reject) {// Have a THEN object const startTime = date.now (); setTimeout( () => resolve(Date.now() - startTime), this.timeout ); } } (async () => { const sleepTime = await new Sleep(1000); Console. log(sleepTime); }) (); / / 1000Copy the code
4. Error handling mechanism
4.1 Async throws an error, which causes the returned Promise object to change to the reject state.
Async function f() {throw new Error(' something went wrong '); //await new Promise(function (resolve, reject) {// throw new Error(' something went wrong '); / /}); } f().then(v => console.log(v), e => console.log(e)Copy the code
4.2 To prevent errors, use try catch, which does not prevent the following run
Async function f() {try {await new Promise(function (resolve, reject) {throw new Error(' something went wrong '); }); } catch(e) { } return await('hello world'); }Copy the code
4.3 If there are multiple await commands, they can be placed in one try… Catch in the structure
Why is there an await/async distinction between await and promise
Catch error
Combined with the for… Use with of
Note: What can be executed concurrently does not have to be run secondary, so this is also a disadvantage
Promise’s downside: Solves the problem of callback hell,
Article is not fast output, but in polishing.
5. Pay attention to the point
5.1 It is best to place await command in try… Catch is followed in a catch block or await
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); Another writing}} / / async function myFunction () {await somethingThatReturnsAPromise (). The catch (function (err) { console.log(err); }); }Copy the code
5.2 Asynchronous operations following more than one await command, if there is no secondary relationship, it is better to have them all firing at the same time, i.e. without await
5.3 Await command can only be used in async functions. If used in normal functions, it will report an error
To run secondary, you can use for… of
async function dbFuc(db) { let docs = [{}, {}, {}]; for (let doc of docs) { await db.post(doc); }}Copy the code
It’s best not to use forEach because it’s concurrent
Function dbFuc(db) {// Async let docs = [{}, {}, {}]; ForEach (async function (doc) {await db.post(doc); //await can only be written inside async functions}); }Copy the code
Another way to write a secondary run is to use Reduce
async function dbFuc(db) {
let docs = [{}, {}, {}];
await docs.reduce(async (_, doc) => {
await _;
await db.post(doc);
}, undefined);
}
Copy the code
If you really want multiple requests to execute concurrently, you can use the promise.all method
async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = await Promise.all(promises); console.log(results); Async function dbFuc(db) {let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = []; for (let promise of promises) { results.push(await promise); } console.log(results); }Copy the code
5.4 Async functions can keep the run stack
const a = async () => { await b(); c(); }; While b() is running, a() is suspended and the context is saved. Once b() or c() reports an error, the error stack will include a(). const a = () => { b().then(() => c()); }; Function A runs an asynchronous task b() internally. Function a() does not break while b() is running, but continues execution. By the time b() is finished, it is possible that a() will have already finished running and the context in which b() is running will have disappeared. If b() or c() reports an error, the error stack will not include a().Copy the code
6. The implementation principle of async function
The principle of the async function is to package the Generator function and the automatic actuator in one function.
async function fn(args) { // ... } function fn(args) {return spawn(function* () {//Generator function //... }); } // autoexecute function spawn(genF) {return new Promise(function(resolve, reject) {const gen = genF(); function step(nextF) { let next; try { next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); }Copy the code
7. Comparison with other asynchronous processing methods
Suppose a series of animations are deployed on a DOM element, and the first animation ends before the next one begins. If one of the animations fails, it stops executing and returns the value of the last successful animation.
Promise
Function chainAnimationsPromise(elem, animations) {// let ret = null; // Create an empty Promise. Let p = promise.resolve (); For (let anim of animations) {p = p.hen (function(val) {ret = val; return anim(elem); // Return a promise object;}); } // Return a Promise return with error catch deployed p.catch(function(e) {/* ignore error, */}).then(function() {return ret; }); }Copy the code
Generator
function chainAnimationsGenerator(elem, animations) { return spawn(function*() { let ret = null; try { for(let anim of animations) { ret = yield anim(elem); }} catch(e) {/* ignore error, */} return ret; }); } complexCopy the code
async
async function chainAnimationsAsync(elem, animations) { let ret = null; //async = try catch try {for(let anim of animations) {ret = await anim(elem); }} catch(e) {/* ignore error, */} return ret; }Copy the code
The implementation of async functions is the most concise and semantically appropriate, with almost no semantically irrelevant code. It provides the autoeffector in Generator writing at the language level, without exposing it to the user, so the amount of code is minimal.
Examples of 8.
- Complete asynchronous operations in sequence
For example, you could remotely read a set of urls in sequence and then print the results in the order they were read
Promise In joint Reduce mode, the remote request is secondary and the result is output in sequence
Const textPromises = urls.map(URL => {return fetch(URL).then(response =>) response.text()); }); // Output textPromises. Reduce ((chain, textPromise) => { return chain.then(() => textPromise) .then(text => console.log(text)); }, Promise.resolve()); }Copy the code
Await the joint for… In the “of” mode, the remote request is sent and the result is output in sequence
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); }}Copy the code
Remote requests are issued concurrently, resulting in sequential output
Async function logInOrder(urls) {// Concurrently read remote URL const textPromises = urls.map(async URL => {const response = await fetch(url); return response.text(); }); For (const textPromise of textPromises) {console.log(await textPromise); }}Copy the code
Read more:
Es6.ruanyifeng.com/#docs/promi…
Es6.ruanyifeng.com/#docs/async…