Imagine the following JavaScript:
async function f(x)
{
var d = await NetworkRequest(x);
d.ProcessResponse();
}
async function g()
{
f(x);
// Is network io completed now?
}
NetworkRequest is a genuinely async function, one that completes asynchronously. Question - by the time f() returns, can one be sure that ProcessResponse is done?
What if g is not declared as async - will that make a difference?
Async functions appear to be blocking, but they actually aren't. They return a promise that resolves to their return value, because they're still asynchronously executing. If you don't return anything, it'll just return a promise that doesn't resolve to anything. In g, you still have to await f(x).
Question - by the time f() returns, can one be sure that ProcessResponse is done?
Absolutely not.
f is declared async, it returns a promise and hands control back to g as soon as it goes to sleep while it awaits another promise.
That is before ProcessResponse is even called.
What if g is not declared as async - will that make a difference?
No
This can be demonstrated:
const obj = {
ProcessResponse: () => console.log("Process Response")
};
function NetworkRequest() {
return new Promise( res => setTimeout(() => res(obj), 1000) );
}
async function f(x)
{
console.log("f, before await");
var f = await NetworkRequest(x);
console.log("f, after await");
f.ProcessResponse();
console.log("f, after Process Response");
}
async function g()
{
console.log("g, before f");
f(x);
console.log("g, after f");
}
const x = "global";
g();
Related
I have the following loop:
while (true) {
await f();
await g();
}
where f and g are defined as follows:
async function f() {
await Promise.all([SOME_REQUEST_F, sleep(1000)])
}
async function g() {
await Promise.all([SOME_REQUEST_G, sleep(5000)])
}
Also sleep is defined as follows:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
My intention is to have SOME_REQUEST_F awaited every one second, and SOME_REQUEST_G awaited every five seconds, hence wrapping them in f and g.
However, currently g is blocking the re-awaiting of f within the loop.
How to define sleep, such that if used in g, it blocks re-execution of g, but not that of f? Is there a better way to do what I want?
Use two while loops instead:
(async () => {
setTimeout(async () => {
while (true) {
await f();
}
});
while (true) {
await g();
}
})();
The setTimeout there is needed to allow both whiles to run concurrently, so that the initialization of one doesn't block the other from starting.
I want to repeatedly call an async function once it completes execution.
The async function invokes its callback argument upon completion, so I can accomplish this by recursively calling the function with the callback:
const fn = () => asyncFn(fn);
However, since NodeJS has stopped supporting tail call optimization, this method will eventually cause stack overflow.
A better way is to use ES6 Promise:
async function fn() {
while (true) {
await new Promise(asyncFn);
}
}
Is there any other way to do it? How would it be done before the introduction of Promise?
I think your initial assumption about the recursive function is incorrect. When you call an async function the callback is queued and the function continues and returns. Once the async function resolves the function is called again, but this is after it has already returned so the stack doesn't wind up.
You can see that here where we get the start and end of the function:
const runAsync = () => {
console.log("starting async function")
setTimeout(() => {
let v = runAsync()
console.log("return val", v)
}, 1000)
return "async function return"
}
console.log("return: ", runAsync())
If the stack was winding up with this, you would never see the return value. You would just see the logs starting async function for each call. This is the behavior seen here where the stack does overflow:
const recursiveFn = () => {
console.log("starting function")
let v = recursiveFn()
console.log("return val", v)
return "test" // never gets here
}
console.log("return: ", recursiveFn())
I have an async function f that calls another async function g. To test if f calls g, I'm stubbing g using sinon and assert it's been called using should.js.
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
But this test passes even when I forget to use await when calling g in f.
One of the ways to catch this error is to make the stub return a dummy promise and check if its then is called.
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
But this is a bit bothersome. Are there any convenient ways to do this?
Your example for f shows flawed code design which becomes more obvious if you write the same function without async/await syntax:
f(n) {
return g(n).then(()=>{});
}
This achieves the same behavior - whether g resolved becomes hard to tell (assuming you don't know if f returned g's promise, which is the same as not knowing whether f awaited g). If f is not interested in the result of g it should just simply return it, not hide it. Then you can simply test for the result.
If your point is that f might have to trigger several async calls sequentially awaiting several g_1, g_2,... to resolve, then you can build a test chain by asserting in the stub of g_n+1 that the dummy-promise of g_n has been resolved. In general your approach to test a dummy-promise for its status is fine.
Instead of stubbing then, you're best off stubbing g in such a way that it sets some boolean on the next event loop iteration. Then, you can check this boolean after calling f to make sure f waited for it:
it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
Edit: Of course, this isn't a perfect guarantee because you could have f wait on any promise that waits at least as long as it takes for g to resolve. Like so:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}
This would cause the test I wrote to pass, even though it's still incorrect. So really it comes down to how strict you're trying to be with your tests. Do you want it to be literally impossible to have a false positive? Or is it ok if some obvious trickery can potentially throw it off?
In most cases I find that the latter is ok, but really that's up to you and/or your team.
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();
If I have an async function, do I have to repeatedly use the await keyword down the call chain, even if the methods being invoked are marked as async and use the await keyword themselves?
async function foo() {
const result = await bar(); // is this await required?
console.log(result);
}
async function bar() {
console.log('waiting...');
return await new Promise(resolve => setTimeout(() => resolve('result'), 1000)) ;
}
You do if you want the promise to be resolved.
Because
const result = bar();
is perfectly valid: it returns a promise.
Using await, you instruct the engine to wait for the promise to be resolved (or fail) and get the result. There are many valid cases where you would want to deal with the promise itself (for example adding operations) even inside an async function.
No you dont:
async function foo() {
const result = await baz(); //this is just required
console.log(result);
}
async function baz(){//mustnt be async
return bar();//simply return, no await required
}
async function bar() {//mustnt be async
console.log('waiting...');
return new Promise(resolve => setTimeout(() => resolve('result'), 1000)) ;//this isnt
}
You just need to await the promise once at the highest level ( if youre not planning to change the data somewhere).
The upper example does not need to be async , however this has to:
async function buy(product){
if(await isAvailableNow()){
return buy();//buy is a promise
}else{
return buySomewhereElse();//another promise
}
}
If I have an async function, do I have to repeatedly use the await keyword down the call chain
If the function is async and you want to run your instructions (that follows the call) AFTER the async function is done, then yes. Always. You have used
return await new Promise(...);
in bar() which is a strange thing. The await is not required here because you have defined the function as async function bar() { ... }. It implicitly returns with a Promise.resolve(...) object. So at the end, no matter of what you do in the return statement, you always get that object.
So when calling bar() as
const result = bar();
// stmt
then result contains a promise object which is doing the task in bar() function asynchronously AND runs the stmt after it. So the stmt is executed, even if the bar() function is not done yet.
To resolve it, you have to use await at the call...
const result = await bar();
// stmt
In this situation, stmt is only executed if bar is done.
If you want the result of the Promise before solving the async you'll need the await. The only function of the await is wait a Promise to be solved, but await is not mandatory in async functions, is just for that case. Just think "I have a Promise? I need this solved before the async?" If the answer was a double yes, so use await.