I’m Garfield at 🐱. If you like my article, please feel free to like it and share it
Async /await is an executor of a generator. To implement async/await we need to understand what a generator is.
1. What are generator functions
Generator is a function that generates an iterator, and the yield expression is a token that pauses execution. The next method of the iterator object must be called to resume execution of the function so that the pointer moves to the next state.
function *gen() {
yield 1;
yield 2;
return "ending";
}
let g = gen();
g.next(); // {value: 1, done: false}
g.next(); // {value: 2, done: false}
g.next(); // {value: 'ending', done: true}
g.next(); // {value: undefined, done: true}
Copy the code
In the above code, the gen function is called first to return an iterator, and the next method is called four times.
On the first invocation, the generator function starts executing until the first yield expression is encountered, and then evaluates the expression following the yield expression as the value of the value attribute, which is false, indicating that the traversal is not complete.
On the second invocation, the generator function takes the input (if any) of the next method as the return value of the yield expression, executes to the next yield expression where the last yield expression left off, and evaluates the expression after the yield expression. Returned as the value of the value attribute, the done attribute has a value of false, indicating that the traversal is not finished.
On the third call, the generator function takes the input (if any) of the next method as the return value of the yield expression, and then executes from where the last yield expression left off until the return statement (or the end of the function if there is no return statement). The value of the expression following the return statement is then returned as the value of the value attribute. The value of the done attribute is true, indicating that the traversal is complete.
The next method returns the value of the object as undefined and the done property as true. Any subsequent calls to the next method will return this value.
Note:
yield
The expression after the expression will only be innext
Method is evaluated when it moves the pointer to this line;yield
The expression itself does not return a value,next
A method can take an argument that will be used as the previous oneyield
The return value of the expression;
The generator functions are actually very similar to the Iterator interface and can use for… Of traversal, which can also be expanded into an array using the extension operator. In contrast to the Iterator interface, generator has a next method, a return method to terminate subsequent processes, and a throw method to throw errors.
2. Simplest actuator
With this in mind, we can implement a generator actuator. For example, we have the following generator function:
const fullfilled = (param) = > {
return new Promise(resolve= > setTimeout(resolve, 1000, param))
}
function *effect() {
const a = yield fullfilled("Test = = = = = = 1");
console.log(a);
const b = yield fullfilled("= = = test 2 = = =");
console.log(b);
}
Copy the code
First, let’s see how manual execution works:
const gen = effect();
// Start executing generator
gen.next().value.then(res= > {
// Use the result of the first Promise as the yield expression return value
// Print "=== test 1==="
gen.next(res).value.then(res= > {
// Return the result of the second Promise as the yield expression
// Print "=== test 2==="gen.next(res); })})Copy the code
With this in mind, implementing the actuator is easy:
function asyncToGenerator(fn) {
const gen = fn();
let step = (val) = > {
const { value, done } = gen.next(val);
// If the iteration is not finished, then method is used to get the result of the previous step
// Then recursively call step for the next iteration! done && value.then(step); } step(); }Copy the code
3. Return to the Promise
The above code just implements the executor, not async/await. We know that async will return the value of the return inside the function wrapped in a Promise, so we implement this function:
function asyncToGenerator(fn) {
// Return a Promise instance
return new Promise((resolve, reject) = > {
const gen = fn();
let step = (val) = > {
const { value, done } = gen.next(val);
if (done) {
// If the iteration ends, resolve the final result
// undefined if the function returns no value
resolve(value);
} else {
// If the iteration is not finished, then method is used to get the result of the previous step
// If the Promise succeeds, step is recursively called for the next iteration
// If the Promise fails, reject is rejectedvalue.then(step, reject); } } step(); })}Copy the code
4. Support non-Promise types
Normally, the right side of the await command is a Promise object. If it is not a Promise object, the corresponding value is returned:
function asyncToGenerator(fn) {
return new Promise((resolve, reject) = > {
const gen = fn();
let step = (val) = > {
const { value, done } = gen.next(val);
if (done) {
resolve(value);
} else {
// This is not the case
// Wrap a Promise directly around a non-PROMISE type
Promise.resolve(value).then(step, reject); } } step(); })}Copy the code
reference
Minimal implementation of Handwriting Async await (20 lines)