Nested async/await functions - javascript

I am trying to understanding how to use async/await in nested manner.
In the example, I am calling function a() and inside that, I want to call b() and ensure that b() resolves before a().
The following is the code and although b() is called, it is not getting resolved.
function a() {
b();
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved a');
}, 2000);
});
}
function b() {
console.log('calling b');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved b');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await a();
console.log(result);
}
asyncCall();
The output of the code above is:
"calling"
"calling b"
"resolved a" (This line shows up after 2 seconds)
Then, I changed to code to include async/await in a(), (everything else is the same)
async function a() {
await b();
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved a');
}, 2000);
});
}
function b() {
console.log('calling b');
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('resolved b');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await a();
console.log(result);
}
asyncCall();
The output of the code above is:
"calling"
"calling b"
"resolved a" (This line shows up after 4 seconds [In the previous example it was 2 seconds])
But in both the case, 'resolved b' is not getting printed.
I have 2 questions:
How do I print 'resolved b' ?
Is it common to have cases like this where an async function (in this case, function "a"), is both the callee (it is called by asyncCall()) and the caller (it is calling b()) ?

How do I print 'resolved b' ?
Your code as it stands completely ignores the resolved value of b. The first example ignores the promise entirely. The second example waits for the promise to resolve but does nothing with the resolved value (not even capture it in a variable).
You have to console.log() it like you did everything else you logged.
You could logthisvalue = await b(). You could b().then(logthisvalue => here). You could modify b() so it logs its own value.
Is it common to have cases like this where an async function (in this case, function "a"), is both the callee (it is called by asyncCall()) and the caller (it is calling b()) ?
Yes.
The async and await keywords are tools to manage promises.
Top level await has poor support so, in general, you can only use await inside an async function.
You have to call an async function for it to do anything.
The async function has to get a promise to await from somewhere and usually that is by calling another function.

Related

How to make async/await actually wait [duplicate]

I read that async functions marked by the async keyword implicitly return a promise:
async function getVal(){
return await doSomethingAync();
}
var ret = getVal();
console.log(ret);
but that is not coherent...assuming doSomethingAsync() returns a promise, and the await keyword will return the value from the promise, not the promise itsef, then my getVal function should return that value, not an implicit promise.
So what exactly is the case? Do functions marked by the async keyword implicitly return promises or do we control what they return?
Perhaps if we don't explicitly return something, then they implicitly return a promise...?
To be more clear, there is a difference between the above and
function doSomethingAync(charlie) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(charlie || 'yikes');
}, 100);
})
}
async function getVal(){
var val = await doSomethingAync(); // val is not a promise
console.log(val); // logs 'yikes' or whatever
return val; // but this returns a promise
}
var ret = getVal();
console.log(ret); //logs a promise
In my synopsis the behavior is indeed inconsistent with traditional return statements. It appears that when you explicitly return a non-promise value from an async function, it will force wrap it in a promise.
I don't have a big problem with it, but it does defy normal JS.
The return value will always be a promise. If you don't explicitly return a promise, the value you return will automatically be wrapped in a promise.
async function increment(num) {
return num + 1;
}
// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));
Same thing even if there's no return! (Promise { undefined } is returned)
async function increment(num) {}
Same thing even if there's an await.
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function incrementTwice(num) {
const numPlus1 = await defer(() => num + 1);
return numPlus1 + 1;
}
// Logs: 5
incrementTwice(3).then(num => console.log(num));
Promises auto-unwrap, so if you do return a promise for a value from within an async function, you will receive a promise for the value (not a promise for a promise for the value).
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function increment(num) {
// It doesn't matter whether you put an `await` here.
return defer(() => num + 1);
}
// Logs: 4
increment(3).then(num => console.log(num));
In my synopsis the behavior is indeed inconsistent with traditional
return statements. It appears that when you explicitly return a
non-promise value from an async function, it will force wrap it in a
promise. I don't have a big problem with it, but it does defy normal
JS.
ES6 has functions which don't return exactly the same value as the return. These functions are called generators.
function* foo() {
return 'test';
}
// Logs an object.
console.log(foo());
// Logs 'test'.
console.log(foo().next().value);
Yes, an async function will always return a promise.
According to the tc39 spec, an async function desugars to a generator which yields Promises.
Specifically:
async function <name>?<argumentlist><body>
Desugars to:
function <name>?<argumentlist>{ return spawn(function*() <body>, this); }
Where spawn "is a call to the following algorithm":
function spawn(genF, self) {
return new Promise(function(resolve, reject) {
var gen = genF.call(self);
function step(nextF) {
var next;
try {
next = nextF();
} catch(e) {
// finished with failure, reject the promise
reject(e);
return;
}
if(next.done) {
// finished with success, resolve the promise
resolve(next.value);
return;
}
// not finished, chain off the yielded promise and `step` again
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); });
});
}
Your question is: If I create an async function should it return a promise or not? Answer: just do whatever you want and Javascript will fix it for you.
Suppose doSomethingAsync is a function that returns a promise. Then
async function getVal(){
return await doSomethingAsync();
}
is exactly the same as
async function getVal(){
return doSomethingAsync();
}
You probably are thinking "WTF, how can these be the same?" and you are right. The async will magically wrap a value with a Promise if necessary.
Even stranger, the doSomethingAsync can be written to sometimes return a promise and sometimes NOT return a promise. Still both functions are exactly the same, because the await is also magic. It will unwrap a Promise if necessary but it will have no effect on things that are not Promises.
Just add await before your function when you call it :
var ret = await getVal();
console.log(ret);
async doesn't return the promise, the await keyword awaits the resolution of the promise. async is an enhanced generator function and await works a bit like yield
I think the syntax (I am not 100% sure) is
async function* getVal() {...}
ES2016 generator functions work a bit like this. I have made a database handler based in top of tedious which you program like this
db.exec(function*(connection) {
if (params.passwd1 === '') {
let sql = 'UPDATE People SET UserName = #username WHERE ClinicianID = #clinicianid';
let request = connection.request(sql);
request.addParameter('username',db.TYPES.VarChar,params.username);
request.addParameter('clinicianid',db.TYPES.Int,uid);
yield connection.execSql();
} else {
if (!/^\S{4,}$/.test(params.passwd1)) {
response.end(JSON.stringify(
{status: false, passwd1: false,passwd2: true}
));
return;
}
let request = connection.request('SetPassword');
request.addParameter('userID',db.TYPES.Int,uid);
request.addParameter('username',db.TYPES.NVarChar,params.username);
request.addParameter('password',db.TYPES.VarChar,params.passwd1);
yield connection.callProcedure();
}
response.end(JSON.stringify({status: true}));
}).catch(err => {
logger('database',err.message);
response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});
Notice how I just program it like normal synchronous particularly at
yield connection.execSql and at yield connection.callProcedure
The db.exec function is a fairly typical Promise based generator
exec(generator) {
var self = this;
var it;
return new Promise((accept,reject) => {
var myConnection;
var onResult = lastPromiseResult => {
var obj = it.next(lastPromiseResult);
if (!obj.done) {
obj.value.then(onResult,reject);
} else {
if (myConnection) {
myConnection.release();
}
accept(obj.value);
}
};
self._connection().then(connection => {
myConnection = connection;
it = generator(connection); //This passes it into the generator
onResult(); //starts the generator
}).catch(error => {
reject(error);
});
});
}

How does Async/await work and why does it not work here

I've been trying to understand how async/await works, all I want to do is have it wait until the value is returned. However, I could not get it to do so with callbacks nor promises nor async/await. What am I doing wrong, and why does async/await not work as I expect it to? (wait with running other code until this promise is resolved)
Many questions like this one link to "introduction pages". I've read them all, I understand what they do, I just don't know how to properly write them out, as I believe I did everything correctly, I'm just missing something
console.log("xcdcxcxcxccxvccvffdgfcd");
thing();
async function thing() {
let c = await otherthing();
console.log("dfasfadsfdasasdfa" + c)
}
async function otherthing() {
await setTimeout(function() {
return new Promise((resolve, reject) => {
let f = "rerewwqfewfasgsadf"
return resolve(f);
})
}, 3000);
}
console.log is supposed to wait until the promised value c is returned, however, it does not seem to work. Why?
Async/await works with functions that return promises. Your otherthing function doesn't return anything.
You can fix the code by returning the promise you are instantiating, like this:
thing();
async function thing() {
let c = await otherthing();
console.log("dfasfadsfdasasdfa" + c)
}
function otherthing() {
return new Promise((resolve, reject) => {
setTimeout(function () {
let f = "rerewwqfewfasgsadf"
resolve(f);
}, 3000)
});
}
You must return the new Promise from the otherthing function, not from the setTimeout callback. You are supposed to construct the promise, in its executor callback start the asynchronous action (setTimeout) and then asynchronously resolve() or reject() the promise.
function otherthing() {
return new Promise((resolve, reject) => {
setTimeout(function(){
let f = "rerewwqfewfasgsadf"
resolve(f);
}, 3000);
});
}
You don't need any async/await here for the function that is essentially only a promise wrapper around setTimeout. See also How do I convert an existing callback API to promises?.
You should move setTimeout inside Promise body and there do resolve(f)
function otherthing() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let f = "rerewwqfewfasgsadf"
resolve(f);
}, 3000);
});
}
async function otherthing has no return statement, so it will always return a promise that resolves to undefined.
await setTimeout(...) doesn't make any sense. You can only await a promise, and setTimeout does not return a promise.
You need to explicitly:
create a new Promise() inside otherthing
resolve() it with the desired value inside the callback function you pass to setTimeout
return the promise from otherthing
Don't create a promise inside the callback function you pass to setTimeout. It is pointless there.

When is async function promise resolved?

I have below code:
let func = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("two");
resolve();
}, 3000);
})
};
let func2 = async () => {
console.log("one");
await func();
console.log("three");
}
(async () => {
await func2();
console.log("main"); // This should never be executed
})()
Noticed func2 never returns a value, the promise returned by func2 should never be fullfilled in my opinion. Hence console.log("main") should never be executed. However it is executed after console.log("three"). Can somebody explain this to me?
Noticed func2 never returns a value, the promise returned by func2 should never be fullfilled in my opinion.
That is not how async functions work. Your func2 returns when it is done executing. The return value of a function with no return statement is the specific value undefined. So, undefined becomes the resolved value of the promise. Remember, in Javascript, undefined is a specific value. It's as if you did return undefined at the end of your function block. So, since undefined is the return value, that becomes the resolved value of the async promise.
To fully cover all the bases, async functions always return a promise and that promise gets a resolved/rejected value one of these ways:
1. When you explicitly return a value from the function. That becomes the resolved value of the promise that the async function returns.
async function test() {
return "hello";
}
test().then(val => {
console.log(val); // "hello"
});
2. When you throw an exception. The exception becomes the reject reason of the promise that the async function returns.
async function test() {
throw new Error("ouch");
}
test().catch(err => {
console.log(err); // Shows error object with message "ouch"
});
3. When you return another promise. That promise is chained to the one that the async function returns and the promise that the async function returns will follow the one you returned (resolve when it resolves with the same value or reject with the same reason).
async function test() {
return new Promise(resolve => {
setTimeout(() => {
resolve("hi");
}, 1000);
});
}
test().then(val => {
console.log(val); // "hi"
});
4. When you return nothing. That is the same as in regular functions and it's equivalent to a return undefined at the end of the function block so the resolved value takes on the value of undefined.
So, this:
async function test() {
console.log("hi");
}
test().then(val => {
console.log(val); // undefined
});
Works exactly the same as this:
async function test() {
console.log("hi");
return undefined;
}
test().then(val => {
console.log(val); // undefined
});
A function that does not explexitly return something, actually returns undefined:
function test() { }
console.log(test());
Its the same with async functions, they also resolve to undefined when no other value was returned.
An async function doesn't need a return value in order to resolve. It is considered resolved when it finishes executing without an error.
If you throw new Error() inside of func2, console.log("main") will never be executed.
When you don't return from a function it implicitly returns a default value — normally undefined. So the promise returned by func2 will still resolve when the function returns.
From MDN:
A function without a return statement will return a default value. In
the case of a constructor called with the new keyword, the default
value is the value of its this parameter. For all other functions, the
default return value is undefined.
You can see this in your code if you alter it to this:
(async () => {
func2()
.then(d => console.log(d));
})()

async function - await not waiting for promise

I'm trying to learn async-await. In this code -
const myFun = () => {
let state = false;
setTimeout(() => {state = true}, 2000);
return new Promise((resolve, reject) => {
setTimeout(() => {
if(state) {
resolve('State is true');
} else {
reject('State is false');
}
}, 3000);
});
}
const getResult = async () => {
return await myFun();
}
console.log(getResult());
why am I getting output as -
Promise { <pending> }
Instead of some value? Shouldn't the getResult() function wait for myFun() function resolve it's promise value?
If you're using async/await, all your calls have to use Promises or async/await. You can't just magically get an async result from a sync call.
Your final call needs to be:
getResult().then(response => console.log(response));
Or something like:
(async () => console.log(await getResult()))()
What you need to understand is that async/await does not make your code run synchronously, but let's you write it as if it is:
In short: The function with async in front of it is literally executed asynchronously, hence the keyword "async". And the "await" keyword wil make that line that uses it inside this async function wait for a promise during its execution. So although the line waits, the whole function is still run asynchronously, unless the caller of that function also 'awaits'...
More elaborately explained: When you put async in front of a function, what is actually does is make it return a promise with whatever that function returns inside it. The function runs asynchronously and when the return statement is executed the promise resolves the returning value.
Meaning, in your code:
const getResult = async () => {
return await myFun();
}
The function "getResult()" will return a Promise which will resolve once it has finished executing. So the lines inside the getResult() function are run asynchronously, unless you tell the function calling getResult() to 'await' for it as well. Inside the getResult() function you may say it must await the result, which makes the execution of getResult() wait for it to resolve the promise, but the caller of getResult() will not wait unless you also tell the caller to 'await'.
So a solution would be calling either:
getResult().then(result=>{console.log(result)})
Or when using in another function you can simply use 'await' again
async function callingFunction(){
console.log(await(getResult());
}
This is my routine dealing with await and async using a Promise with resolve and reject mechanism
// step 1 create a promise inside a function
function longwork()
{
p = new Promise(function (resolve, reject) {
result = 1111111111111 // long work here ;
if(result == "good"){
resolve(result);
}
else
{
reject("error ...etc")
}
})
return p
}
// step 2 call that function inside an async function (I call it main)and use await before it
async function main()
{
final_result = await longwork();
//..
}
//step 3 call the async function that calls the long work function
main().catch((error)=>{console.log(error);})
Hope that saves someone valuable hours
What hasn't been mentioned in this discussion are the use-case implications of the behaviour. The key thing, as I see it, is to consider what you are planning to do with the output from the top level, truly asynchronous function, and where you are planning to do that.
If you are planning to consume the output immediately, i.e. within the "async" function that is awaiting the return of the top level asynchronous function, and what you do with the output has no implication for other functions deeper in the call stack, then it does not matter that the deeper functions have moved on. But if the output is needed deeper in the call stack, then you need use "async" functions making await calls all the way down the stack to that point. Once you reach a point in the call stack where the function does not care about the asynchronous output, then you can stop using async functions.
For example, in the following code, function B uses the stuff returned from function A so is declared "async" and awaits A(). Function C() calls B(), is returned a Promise, but can move straight on before that promise is resolved because it is not interested in A()'s stuff, nor what's done with it. So C does not need to be declared as async, nor await B().
function A() {
return new Promise((resolve, reject) => {
//do something slow
resolve (astuff)
}
}
async function B() {
var bstuff = await A();
dosomethingwith(bstuff);
return;
}
function C() {
B();
dontwaitmoveon();
...
return;
}
In this next example, C() does use A()'s stuff, so needs to wait for it. C() must be declared "async" and await B(). However D() does not care about A()'s stuff, nor what's done with it, so moves on once C() returns its promise.
function A() {
return new Promise((resolve, reject) => {
//do something slow
resolve (astuff)
}
}
async function B() {
var bstuff = await A();
dosomething();
return bstuff;
}
async function C() {
var cstuff = await B();
dosomethingwith(cstuff);
...
return;
}
function D() {
C();
dontwaitmoveon();
...
return;
}
Since figuring this out, I have tried to design my code so the stuff returned by the asynchronous function is consumed as close as possible to the source.
Though your "getResult" function is async and you have rightly made an await call of myFun, look at the place where you call the getResult function, it is outside any async functions so it runs synchronously.
So since getResult called from a synchronous point of view, as soon as it is called, Javascript synchronously gets whatever result is available at the moment, which is a promise.
So returns from an async function cannot be forced to await(very important), since they are synchronously tied to the place of origin of the call.
To get what you want you can run the below,
async function getResult() {
const result = await myFun();
console.log(result);
//see no returns here
}
getResult();

Difference between async/await and ES6 yield with generators

I was just reading this fantastic article «Generators» and it clearly highlights this function, which is a helper function for handling generator functions:
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
which I hypothesize is more or less the way the async keyword is implemented with async/await. So the question is, if that is the case, then what the heck is the difference between the await keyword and the yield keyword? Does await always turn something into a promise, whereas yield makes no such guarantee? That is my best guess!
You can also see how async/await is similar to yield with generators in this article where he describes the 'spawn' function ES7 async functions.
Well, it turns out that there is a very close relationship between async/await and generators. And I believe async/await will always be built on generators. If you look at the way Babel transpiles async/await:
Babel takes this:
this.it('is a test', async function () {
const foo = await 3;
const bar = await new Promise(resolve => resolve('7'));
const baz = bar * foo;
console.log(baz);
});
and turns it into this
function _asyncToGenerator(fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
return step("next", value);
}, function (err) {
return step("throw", err);
});
}
}
return step("next");
});
};
}
this.it('is a test', _asyncToGenerator(function* () { // << now it's a generator
const foo = yield 3; // <<< now it's yield, not await
const bar = yield new Promise(resolve => resolve(7));
const baz = bar * foo;
console.log(baz);
}));
you do the math.
This makes it look like the async keyword is just that wrapper function, but if that's the case then await just gets turned into yield, there will probably be a bit more to the picture later on when they become native.
You can see more of an explanation for this here:
https://www.promisejs.org/generators/
yield can be considered to be the building block of await. yield takes the value it's given and passes it to the caller. The caller can then do whatever it wishes with that value (1). Later the caller may give a value back to the generator (via generator.next()) which becomes the result of the yield expression (2), or an error that will appear to be thrown by the yield expression (3).
async-await can be considered to use yield. At (1) the caller (i.e. the async-await driver - similar to the function you posted) will wrap the value in a promise using a similar algorithm to new Promise(r => r(value) (note, not Promise.resolve, but that's not a big deal). It then waits for the promise to resolve. If it fulfills, it passes the fulfilled value back at (2). If it rejects, it throws the rejection reason as an error at (3).
So the utility of async-await is this machinery that uses yield to unwrap the yielded value as a promise and pass its resolved value back, repeating until the function returns its final value.
what the heck is the difference between the await keyword and the yield keyword?
The await keyword is only to be used in async functions, while the yield keyword is only to be used in generator function*s. And those are obviously different as well - the one returns promises, the other returns generators.
Does await always turn something into a promise, whereas yield makes no such guarantee?
Yes, await will call Promise.resolve on the awaited value.
yield just yields the value outside of the generator.
tl;dr
Use async/await 99% of the time over generators. Why?
async/await directly replaces the most common workflow of promise chains allowing code to be declared as if it was synchronous, dramatically simplifying it.
Generators abstract the use case where you would call a series of async-operations that depend on each other and eventually will be in a "done" state. The most simple example would be paging through results that eventually return the last set but you would only call a page as needed, not immediately in succession.
async/await is actually an abstraction built on top of generators to make working with promises easier.
See very in-depth Explanation of Async/Await vs. Generators
Try this test programs which I used to understand await/async with promises.
Program #1: without promises it doesn't run in sequence
function functionA() {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
return 10;
}, 15000);
}
function functionB(valueA) {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return 20 + valueA;
}, 10000);
}
function functionC(valueA, valueB) {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return valueA + valueB;
}, 10000);
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
Program #2: with promises
function functionA() {
return new Promise((resolve, reject) => {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
// return 10;
return resolve(10);
}, 15000);
});
}
function functionB(valueA) {
return new Promise((resolve, reject) => {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return resolve(20 + valueA);
}, 10000);
});
}
function functionC(valueA, valueB) {
return new Promise((resolve, reject) => {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return resolve(valueA + valueB);
}, 10000);
});
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
In many ways, generators are a superset of async/await. Right now async/await has cleaner stack traces than co, the most popular async/await-like generator based lib. You can implement your own flavor of async/await using generators and add new features, like built-in support for yield on non-promises or building it on RxJS observables.
So, in short, generators give you more flexibility and generator-based libs generally have more features. But async/await is a core part of the language, it's standardized and won't change under you, and you don't need a library to use it. I have a blog post with more details on the difference between async/await and generators.
The yield+gen.next()-as-a-language-feature can be used to describe (or implement) the underlying control-flow that await-async has abstracted away.
As other answers suggest, await-as-a-language-feature is (or can be thought of) an implementation on top of yield.
Here is a more intutive understanding for that:
Say we have 42 awaits in an async function, await A -> await B -> ...
Deep down it is equivalent to having yield A -> tries resolve this as a Promise [1]
-> if resolvable, we yield B, and repeat [1] for B
-> if not resolveable, we throw
And so we end up with 42 yields in a generator. And in our controller we simply keep doing gen.next() until it is completed or gets rejected. (ie this is the same as using await on an async function that contains 42 await.)
This is why lib like redux-saga utilizes generator to then pipe the promises to the saga middleware to be resolved all at one place; thus decoupling the Promises constructions from their evaluations, thus sharing close resemblance to the Free Monad.
The idea is to recursively chain then() calls to replicate the behavior of await which allows one to invoke async routines in a synchronous fashion. A generator function is used to yield back control (and each value) from the callee to the caller, which happens to be the _asyncToGenerator() wrapper function.
As mentioned above, this is the trick that Babel uses to create polyfills. I slightly edited the code to make it more readable and added comments.
(async function () {
const foo = await 3;
const bar = await new Promise((resolve) => resolve(7));
const baz = bar * foo;
console.log(baz);
})();
function _asyncToGenerator(fn) {
return function () {
let gen = fn(); // Start the execution of the generator function and store the generator object.
return new Promise(function (resolve, reject) {
function step(func, arg) {
try {
let item = gen[func](arg); // Retrieve the function object from the property name and invoke it. Similar to eval(`gen.${func}(arg)`) but safer. If the next() method is called on the generator object, the item value by the generator function is saved and the generator resumes execution. The value passed as an argument is assigned as a result of a yield expression.
if (item.done) {
resolve(item.value);
return; // The executor return value is ignored, but we need to stop the recursion here.
}
// The trick is that Promise.resolve() returns a promise object that is resolved with the value given as an argument. If that value is a promise object itself, then it's simply returned as is.
return Promise.resolve(item.value).then(
(v) => step("next", v),
(e) => step("throw", e)
);
} catch (e) {
reject(e);
return;
}
}
return step("next");
});
};
}
_asyncToGenerator(function* () { // <<< Now it's a generator function.
const foo = yield 3; // <<< Now it's yield, not await.
const bar = yield new Promise((resolve, reject) => resolve(7)); // <<< Each item is converted to a thenable object and recursively enclosed into chained then() calls.
const baz = bar * foo;
console.log(baz);
})();

Categories