The introduction
Async /await is a great syntactic candy that is the ultimate solution to the asynchronous problem. Take it literally. Async means asynchronous and await means to wait, so understand async to declare that a function is asynchronous and await to wait for an asynchronous method to complete.
Async role
Async declares that function is an asynchronous function that returns a Promise object, and callbacks can be added using the then method. The value returned by the return statement inside the async function becomes an argument to the then method callback function.
async function test() {
return 'test';
}
console.log(test); // [AsyncFunction: test] Async functions are instances of the [' AsyncFunction '] constructor
console.log(test()); // Promise { 'test' }
Async returns a Promise object
test().then(res= >{
console.log(res); // test
})
Async returns a undefined promise object if async returns no value
async function fn() {
console.log('No return');
}
console.log(fn()); // Promise { undefined }
// Async returns the same value as promise.resolve (), wrapping the return value as a Promise object, or returning the undefined Promise object if no value is returned
Copy the code
await
The await operator can only be used inside async function. If a Promise is passed to an await operator, await will wait for the normal processing of the Promise to complete and return its processing result, that is, it will block the following code, waiting for the Promise object result. If you are waiting for something other than a Promise object, the value itself is returned.
async function test() {
return new Promise((resolve) = >{
setTimeout(() = > {
resolve('test 1000');
}, 1000); })}function fn() {
return 'fn';
}
async function next() {
let res0 = await fn(),
res1 = await test(),
res2 = await fn();
console.log(res0);
console.log(res1);
console.log(res2);
}
next(); Res1 is waiting for the promise result and blocks the following code.
Copy the code
Error handling
If an asynchronous operation following await fails, the Promise object returned by the async function is rejected.
async function test() {
await Promise.reject('Wrong')}; test().then(res= >{
console.log('success',res);
},err= >{
console.log('err ',err);
})
// Err
Copy the code
The way to prevent errors is to put it in a try… Inside the catch block.
async function test() {
try {
await new Promise(function (resolve, reject) {
throw new Error('Wrong');
});
} catch(e) {
console.log('err', e)
}
return await('It worked');
}
Copy the code
Asynchronous operations following multiple await commands are best fired at the same time if there is no secondary relationship (i.e. no dependency).
let foo = await getFoo();
let bar = await getBar();
GetBar is executed only after getFoo is done
// Trigger the ↓
/ / write one
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
/ / write two
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
Copy the code
Async/await an advantage
The advantage of async/await is to handle then chains consisting of multiple promises. The problem of using THEN to handle callback hell was mentioned in the previous Promise article. Async /await is a further optimization of promises. Assume that a service has multiple steps, and each step is asynchronous and depends on the execution result of the previous step.
// Suppose the form is submitted through two validation interfaces
async function check(ms) { // imitate asynchrony
return new Promise((resolve) = >{
setTimeout(() = > {
resolve(`check ${ms}`); }, ms); })}function check1() {
console.log('check1');
return check(1000);
}
function check2() {
console.log('check2');
return check(2000);
}
// -------------promise------------
function submit() {
console.log('submit');
// After two checks, multi-level association promise values are deeply nested
check1().then(res1= >{
check2(res1).then(res2= >{
/* * Submit the request */
})
})
}
submit();
// -------------async/await-----------
async function asyncAwaitSubmit() {
let res1 = await check1(),
res2 = await check2(res1);
console.log(res1, res2);
/* * Submit the request */
}
Copy the code
The principle of
Async functions are implemented by wrapping Generator functions and automatic actuators in one function.
async function fn(args) {
// ...
}
/ / is equivalent to
function fn(args) {
return spawn(function* () {
// ...
});
}
Copy the code
The /* * Generator function is a encapsulated asynchronous task, or a container for asynchronous tasks. * Where asynchronous operations need to be paused, use yield statements * To call a Generator function that returns a pointer object (this is the difference from normal functions). Calling the next method on the pointer object moves the internal pointer. * The next method is used to execute Generator functions in stages. Each time the next method is called, an object is returned representing information about the current stage (value and Done properties). The value attribute is the value of the expression following the yield statement and represents the value of the current phase; The done attribute is a Boolean value indicating whether the Generator has completed execution, that is, whether there is another phase. * /
// Understand the usage of generator
function* Generator() {
yield '1';
yield Promise.resolve(2);
return 'ending';
}
var gen = Generator(); // Return pointer Object [Generator] {}
let res1 = gen.next();
console.log(res1); // Return the current stage value {value: '1', done: false}
let res2 = gen.next();
console.log(res2); {value: Promise {2}, done: false}
res2.value.then(res= >{
console.log(res); / / 2
})
let res3 = gen.next();
console.log(res3); // { value: 'ending', done: true }
let res4 = gen.next();
console.log(res4); // { value: undefined, done: true }
Copy the code
Generator implements async functions
// Accept a Generator function as a parameter
function spawn(genF) {
// Return a function
return function() {
// Generate a pointer object
const gen = genF.apply(this.arguments);
// Return a promise
return new Promise((resolve, reject) = > {
// Key has two values, next and throw, corresponding to gen's next and throw methods respectively
The arg argument is used to yield the promise resolve value to the next yield
function step(key, arg) {
let result;
// If an error is detected, reject the promise
try {
result = gen[key](arg)
} catch (error) {
return reject(error)
}
// gen.next() returns {value, done}
const { value, done } = result;
if (done) {
// If this is already done, resolve the promise
return resolve(value)
} else {
// Call gen.next() every time except at the end
return Promise.resolve(
// This value corresponds to the promise after yield
value
).then((val) = >step("next", val),(err) = >step("throw", err))
}
}
step("next")}}}Copy the code
test
function fn(nums) {
return new Promise(resolve= > {
setTimeout(() = > {
resolve(nums)
}, 1000)})}/ / async function
async function testAsync() {
let res1 = await fn(1);
console.log(res1); / / 1
let res2 = await fn(2);
console.log(res2); / / 2
return res2;
}
let _res = testAsync();
console.log('testAsync-res',_res); // Promise
_res.then(v= >console.log('testAsync-res',v)) / / 2
/ / function Generator
function* gen() {
let res1 = yield fn(3);
console.log(res1); / / 3
let res2 = yield fn(4);
console.log(res2); / / 4
// let res3 = yield Promise.reject(5);
// console.log(res3);
return res2;
}
let _res2 = spawn(gen)();
console.log('gen-res',_res2); // Promise
_res2
.then(v= >console.log('gen-res',v)) / / 4
.catch(err= >{console.log(err)}) // res3 execution throws an exception
Copy the code
conclusion
Async /await syntax candy makes asynchronous code clearer and more readable, so roll it up. Generator can read about it if interested.