I have this one line of code but I cant seem to understand how this works
let p1 = p.then(a => [p1])
let [p2] = await p1
console.log(p1===p2)
I cant understand the first line. how can then get a hold of p1. afaik, a => [p1] inside then is executed immediately, but at the time of execution ,surely p1 was not formed yet. so, how come that code work? not only that, it somehow manages to correctly refer to itself. can anybody help understand what's going on here?
thanks
a => [p1] inside then is executed immediately
No, it's not. A .then called on a fulfilled Promise does not get called immediately. p1 is initialized and the Promise is assigned to it before the .then callback runs, so there's no problem.
This should make it clearer:
(async () => {
const p = Promise.resolve();
console.log('starting');
let p1 = p.then(a => {
console.log('then running');
return [p1];
});
console.log('p1 has now been initialized');
let [p2] = await p1;
console.log(p1===p2);
})();
If it was executed immediately, you'd be correct - p1 hasn't been initialized yet at the point that it's being referenced, and an error would be thrown.
(async () => {
const someObjectWhoseThenRunsImmediately = {
then: (callback) => {
// do something with callback, synchronously...
callback();
}
};
console.log('starting');
let p1 = someObjectWhoseThenRunsImmediately.then(a => {
console.log('then running');
return [p1];
});
console.log('p1 has now been initialized');
let [p2] = await p1;
console.log(p1===p2);
})();
But that's not how Promises work.
Related
I'm hoping my title and simplistic code explains what I'm looking to do. I'm trying to call myFunction(), then let those promises resolve with Promise.all... then, and then execute code after myfunction();
myfunction() {
let x = promiseOneFunction();
let y = promiseTwoFunction();
Promise.all([x, y]).then(values => {
let valOne = = values[0];
let valTwo = values[1];
returnObj = {
fistVal: valOne,
destination: valTwo
}
console.log("return object within promise is ", returnObj);
return returnObj;
});
} // end myfunction
myfunction();
console.log("want to have this log after my function is finished");
Thanks!
The only (sensible) way to run code when a promise completes is to either:
Call then() on the promise
await the promise
This means you need the promise to be available outside myfunction. Currently that function returns nothing. Change it so it returns the promise returned by Promise.all([x, y]).then(...) (which you currently ignore).
return Promise.all([x, y]).then(...)
Then that promise will be available outside the function:
const the_promise = myfunction();
And you can call then on it.
the_promise.then(() => {
console.log("want to have this log after my function is finished");
});
Due to the fact that JS is single threaded, all asynchronous functions will be put in to the event queue, which runs after the main thread has finished processing the current function/block of code.
If you want something to happen strictly after the async function you need to put it in a .then() block chained to the async function or you could use the new async/await syntax. It's the same.
With .then()
myAsyncFunc().then(result => {
console.log(result)
})
With async/await, you first declare your function as an async function
const myAsyncFunc = async () => {
//Content of your function such as Promise.all
//Returns a Promise
}
Then you call it like:
const result = await myAsyncFunc();
console.log(result)
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();
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.
Perhaps i'm not googleing correctly. does a then function without a paramater not block? for instance, you have a promise:
someFunc = () => {
return new Promise((res,rej)=>{
somethingAsync(input).then((val) => res(val))
})
}
in the following implements of our function. would both wait for the someFunc return val?
someFunc().then(dosomethingafter())
someFunc().then((val) => dosomethingafter())
In JS expressions are eagerly evaluated. It means every function argument is evaluated before it's passed.
someFunc().then(dosomethingafter())
is effectively identical to
var tmp = dosomethingafter();
someFunc().then(tmp)
so a function someFunc().then(dosomethingafter()) is invoked before then is called, and its returned result is passed as a parameter.
What you probably meant instead is
someFunc().then(dosomethingafter)
note there is no function call - only a reference to a function is passed to then and it would then be called when a promise is resolved.
It is easier to illustrate this via examples. Your first case:
const fn = (text) => {console.log(text)}
const waiter = () => new Promise((resolve, reject) => {
return setTimeout(() => {
fn('resolved')
resolve()
}, 2000)
})
waiter().then(fn('done'))
Notice that fn got executed first, got evaluated and then the waiter got executed.
Lets look at the 2nd case:
const fn = (text) => {console.log(text)}
const waiter = () => new Promise((resolve, reject) => {
return setTimeout(() => {
fn('resolved')
resolve()
}, 2000)
})
waiter().then(() => fn('done'))
Notice now that we got resolved first and then done.
So the answer to your question is yes in both cases we will wait and execute the someFunc or in the above example the waiter.
The main difference really is when does your dosomethingafter get executed.
In the first case it is right away and then it is passed in the waiter.
In the second case you have a valid promise chain which will first be executed and then once done (and since fn acts as the function handler of the then) it will execute dosomethingafter.
pass doSomethingafter first class
const handleAsJson = response => response.json()
fetch(url)
.then(handleAsJson)
.then(console.log)
I am new to Promises, so I am wondering if this is fine:
this.someFunc()
.then(() => {
alert('loaded')
});
someFunc() = () => {
return new Promise(async(resolve) => {
let obj = await AsyncStorage.getItem('some_val');
//do stuff with obj
resolve('yay, everything's done');
});
};
Or should I always ALWAYS use then with Promises like
return new Promise((resolve) => {
AsyncStorage.getItem('some_val')
.then((obj) => {
// do stuff with obj
resolve('yay')
});
? Is the first approach anti-pattern like? Is it "wrong"? And why?
Adding the async keyword to a function makes it return a Promise.
Ignoring that promise and wrapping it in another one doesn't really make sense. It makes the code confusing to people trying to maintain it.
You could rewrite it as:
someFunc() = async () => {
let obj = await AsyncStorage.getItem('some_val');
//do stuff with obj
return 'yay, everything's done';
};
I just refactored your codes by removing unnecessary things.
someFunc() = async () => {
// when you declare arrow function, it should not have () with its name
// put async before arguments to enable await in the code block below
return new Promise(async(resolve) => {
// you don't need this line because of async and await
// await returns data wrapped by Promise
// as soon as this Promise is resolved or rejected
let obj = await AsyncStorage.getItem('some_val');
// 'await' will return the item that is wrapped by Promise.
// So, you can access the item(the returned data) in this code block.
// but if you want this 'someFunc' to return 'obj'
//do stuff with obj
resolve("yay, everything's done");
// you don't need this line because 'await' will take care of
// 'AsyncStorage.getItem('some_val')'
});
};
After refactoring, you will have code like below.
someFunc = async () => {
// One of the benefit of async and await is that you can use try/catch block.
// So, you can easily console out error without by chaining multiple '.catch' after
// every '.then'.
try{
let obj = await AsyncStorage.getItem('some_val');
// 'obj' will be the value of item 'some_val'
// do stuff with obj
} catch(error) {
console.log(error);
}
return obj; //this will return resolved Promise because of await.
};
If you want to use this obj in another function, you can do something like below.
anotherFunc = async () => {
try{
const resolvedObj = await someFunc();
// Since someFunc will return resolved Promise that wraps the data from
// AsyncStorage, await will assign the data to 'resolvedObj'
// However, if you return 'resolvedObj', this will be 'PromiseĀ {<resolved>:
// dataFromAsyncStorage}'
// do stuff with 'resolvedObj'
} catch(error) {
console.log(error);
}
}