As we all know, async/await is just a syntactic sugar, which is realized based on generator. I write its intermediate principle realization from scratch according to the materials on the Internet.
The generator
Generators are defined by adding * after function when defining functions, like this: function* func(){}. After executing a generator function, we get an iterator, which can yield to suspend the function until the iterator calls the next method. At the same time, next can pass in an argument as yield.
Here we define two asynchronous functions as experimental objects in the future:
function afunc() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve("afunc");
}, 1000);
});
}
function bfunc() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve("bfunc");
}, 2000);
});
}
Copy the code
The first edition
Firstly, two functions are defined respectively, one is to collect the asynchronous functions needed in async functions, and the other is to run the asynchronous functions in the async function body.
function collector(. fn) {
return function () {
for (const item of fn) {
item().then((fulfilled) = >it.next(fulfilled)); }}; }function* async(. fn) {
letinner = collector(... fn);console.log("aaa");
console.log(yield inner());
console.log("bbb");
console.log(yield inner());
}
let it = async(afunc, bfunc);
it.next();
Aaa afunc BBB bfunc
Copy the code
As you can see, it is already possible to place the synchronous method between two asynchronous functions, but there is a problem with this scheme. The function was expected to print in three seconds, but it took two seconds. The reason is that the array was iterated synchronously in the Collector.
The second edition
Since the reason is to execute the collected function synchronously, why don’t I just write it in the function body? So you get this second version that doesn’t work:
function* generator() {
console.log("aaa");
let result = yield afunc().then((fulfilled) = > {
it.next(fulfilled);
});
console.log(result);
let other = yield bfunc().then((fulfilled) = > {
it.next(fulfilled);
});
console.log(other);
}
it = generator();
it.next();
Aaa afunc BBB bfunc
Copy the code
But now that I’ve got what I want, I just need to wrap it up
The third edition
This version is pretty close to writing async/await by defining the order of execution in generator functions, jumping out each time an asynchronous function is executed, and waiting for the asynchronous function to complete before proceeding to the next step.
function* generator() {
let result = yield afunc();
console.log(result);
let other = yield bfunc();
console.log(other);
}
myAwait(generator);
function myAwait(genner, ... args) {
letiter = genner(... args);// Get the iterator of the generator
return new Promise((resolve, reject) = > {
let result; // The result of each pause in iter
/ /! Inner is iterating through iter manually
let inner = function (yield) {
result = iter.next(yield); // Start iterating and pass in the yield as yield to the generator
if (result.done) {
// End of iteration:
resolve(result.value); / / end of Promise
} else {
// If there is no end, wait until the promise ends to continue recursing
return Promise.resolve(result.value).then((fulfilled) = >{ inner(fulfilled); }); }}; inner();// The iterator should not pass arguments the first time
});
}
Copy the code
Just add some error handling and a handwritten async/await is complete.