I am trying to understand the mechanism of async functions. I found some code on MDN docs MDN docs, made some modifications and... cannot fully understand how it works.
var resolveAfter2Seconds = function() {
console.log("starting slow promise");
return new Promise(resolve => {
setTimeout(function() {
resolve(20);
console.log("slow promise is done");
}, 6000);
});
};
var resolveAfter1Second = function() {
console.log("starting fast promise");
return new Promise(resolve => {
setTimeout(function() {
resolve(10);
console.log("fast promise is done");
}, 4000);
});
};
var sequentialStart = async function() {
console.log('==SEQUENTIAL START==');
const slow = await resolveAfter2Seconds();
const fast = await resolveAfter1Second();
console.log(fast);
console.log('why?');
console.log(slow);
}
sequentialStart();
For now I know that if we run this code we will immediately receive '==SEQUENTIAL START==' on the console, then "starting slow promise" then we have a Promise with setTimeout which indicates that 'slow promise is done' will appear after 6 seconds and the resolve(20) callback will be kept in api container since execution stack will be empty.JS gives us 'starting fast promise' and after four seconds we get 'fast promise is done' and then immediately 10, 'why?', 20.
I do not understand: what happens in the background exactly - I know that resolve(20) is kept in api container and the rest of the code are executed, then resolve(10) is also kept in api container and when the execution stack is empty they are returned as the results of resolving their Promises.
But:
What with the timer? 10,why,20 appears long after their timeout passes - resolve 20 appears on the screen long after 6 seconds.
What with order? It seems like they (resolve20 and resolve 10) are ready to be executed and kept in memory until I use them - print them in console in this case? TIME APPEARING and ORDER
I am very determined to understand it correctly.
Perhaps this will help clear things up. Async-await is just syntactic sugar, so your sequentialStart function is the exact same thing as:
var sequentialStart = function() {
console.log('==SEQUENTIAL START==');
resolveAfter2Seconds().then(function(slow) {
resolveAfter1Second().then(function(fast) {
console.log(fast);
console.log('why?');
console.log(slow);
});
});
}
I know that resolve(20) is kept in api container and the rest of the code are executed, then resolve(10) is also kept in api container and when the execution stack is empty they are returned as the results of resolving their Promises
That's not what's happening when using async-await, what your code is doing is the following in this specific order:
Call resolveAfter2Seconds()
await it to resolve and then assign the resolved value to the constant slow
Call resolveAfter1Second()
await it to resolve and then assign the resolved value to the constant fast
Call console.log(fast), then console.log('why?'), then console.log(slow)
It seems like you're expecting the promises to resolve in parallel, as they would if you weren't using async-await but the whole purpose of async-await is to be able to write code with promises as if it was synchronous (i.e. blocking) code without creating a nested mess of then callbacks.
Related
I'm doing some unit testing. The test framework loads a page into an iFrame and then runs assertions against that page. Before each test begins, I create a Promise which sets the iFrame's onload event to call resolve(), sets the iFrame's src, and returns the promise.
So, I can just call loadUrl(url).then(myFunc), and it will wait for the page to load before executing whatever myFunc is.
I use this sort of pattern all over the place in my tests (not just for loading URLs), primarily in order to allow changes to the DOM to happen (e.g. mimick clicking a button, and wait for divs to hide and show).
The downside to this design is that I'm constantly writing anonymous functions with a few lines of code in them. Further, while I have a work-around (QUnit's assert.async()), the test function that defines the promises completes before the promise is run.
I'm wondering if there is any way to get a value from a Promise or wait (block/sleep) until it has resolved, similar to .NET's IAsyncResult.WaitHandle.WaitOne(). I know JavaScript is single-threaded, but I'm hoping that doesn't mean that a function can't yield.
In essence, is there a way to get the following to spit out results in the correct order?
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
};
function getResultFrom(promise) {
// todo
return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
I'm wondering if there is any way to get a value from a Promise or
wait (block/sleep) until it has resolved, similar to .NET's
IAsyncResult.WaitHandle.WaitOne(). I know JavaScript is
single-threaded, but I'm hoping that doesn't mean that a function
can't yield.
The current generation of Javascript in browsers does not have a wait() or sleep() that allows other things to run. So, you simply can't do what you're asking. Instead, it has async operations that will do their thing and then call you when they're done (as you've been using promises for).
Part of this is because of Javascript's single threadedness. If the single thread is spinning, then no other Javascript can execute until that spinning thread is done. ES6 introduces yield and generators which will allow some cooperative tricks like that, but we're quite a ways from being able to use those in a wide swatch of installed browsers (they can be used in some server-side development where you control the JS engine that is being used).
Careful management of promise-based code can control the order of execution for many async operations.
I'm not sure I understand exactly what order you're trying to achieve in your code, but you could do something like this using your existing kickOff() function, and then attaching a .then() handler to it after calling it:
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
}
kickOff().then(function(result) {
// use the result here
$("#output").append(result);
});
This will return output in a guaranteed order - like this:
start
middle
end
Update in 2018 (three years after this answer was written):
If you either transpile your code or run your code in an environment that supports ES7 features such as async and await, you can now use await to make your code "appear" to wait for the result of a promise. It is still programming with promises. It does still not block all of Javascript, but it does allow you to write sequential operations in a friendlier syntax.
Instead of the ES6 way of doing things:
someFunc().then(someFunc2).then(result => {
// process result here
}).catch(err => {
// process error here
});
You can do this:
// returns a promise
async function wrapperFunc() {
try {
let r1 = await someFunc();
let r2 = await someFunc2(r1);
// now process r2
return someValue; // this will be the resolved value of the returned promise
} catch(e) {
console.log(e);
throw e; // let caller know the promise was rejected with this reason
}
}
wrapperFunc().then(result => {
// got final result
}).catch(err => {
// got error
});
async functions return a promise as soon as the first await is hit inside their function body so to the caller an async function is still non-blocking and the caller must still deal with a returned promise and get the result from that promise. But, inside the async function, you can write more sequential-like code using await on promises. Keep in mind that await only does something useful if you await a promise so in order to use async/await, your asynchronous operations must all be promise-based.
If using ES2016 you can use async and await and do something like:
(async () => {
const data = await fetch(url)
myFunc(data)
}())
If using ES2015 you can use Generators. If you don't like the syntax you can abstract it away using an async utility function as explained here.
If using ES5 you'll probably want a library like Bluebird to give you more control.
Finally, if your runtime supports ES2015 already execution order may be preserved with parallelism using Fetch Injection.
Another option is to use Promise.all to wait for an array of promises to resolve and then act on those.
Code below shows how to wait for all the promises to resolve and then deal with the results once they are all ready (as that seemed to be the objective of the question); Also for illustrative purposes, it shows output during execution (end finishes before middle).
function append_output(suffix, value) {
$("#output_"+suffix).append(value)
}
function kickOff() {
let start = new Promise((resolve, reject) => {
append_output("now", "start")
resolve("start")
})
let middle = new Promise((resolve, reject) => {
setTimeout(() => {
append_output("now", " middle")
resolve(" middle")
}, 1000)
})
let end = new Promise((resolve, reject) => {
append_output("now", " end")
resolve(" end")
})
Promise.all([start, middle, end]).then(results => {
results.forEach(
result => append_output("later", result))
})
}
kickOff()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Updated during execution: <div id="output_now"></div>
Updated after all have completed: <div id="output_later"></div>
I've researched many many posts and articles, and documentation. I'm seeking deeper understanding.
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
The await expression causes async function execution to pause until a Promise is settled, [...]
let fetchLocation = () => {
//fetches information from satellite system and
//returns a Vector2 of lat long on earth
return new Promise(resolve => {
setTimeout(() => {
resolve({
lat: 73,
long: -24
});
}, 2000);
});
}
//async functions automatically return a promise.
let main = async() => {
//awaits the promises resolution
let result = await fetchLocation();
console.log("I'm the awaited promise result", result)
//immediately returns a promise as promise pending
return result;
}
console.log(main().then(res => console.log("I'm the original promise, just passed along by the async function, it didn 't a-wait for me", res)))
Before going down this rabbit hole, I knew async/await was syntactical sugar to make async code look synchronous.
I therefore fully expected to see main return the lat long coordinates. Instead it returns a promise.
A couple questions.
Is the MDN documentation wrong? Does it not actually pause execution? It seems that the await keyword does not actually "pause" function execution... invoking main immediately returns a Promise<pending> object. However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop? (And an async return statement is still synchronous.)
So it seems that await unwraps a promise, and async wraps a promise, but you need to use await within async. I get that await might actually be promise.then() underneath the hood but if they want to make it fully synchronous in appearance, why not have the async function return the resolved promise value when used with await? Otherwise async await seems a bit redundant.
Is the MDN documentation wrong?
No
Does it not actually pause execution?
It pauses the execution of the async function, hands a Promise back to the calling function — because it hasn't reached the point in the async function where it knows what to return — then the execution of the calling function (and the rest of the code) continues.
However, after it returns we receive the value of the fulfilled promise in the awaited variable result. I suppose this came from the event loop?
When the event loop stops being busy (running the code that called the async function in the first place and anything else that it picks up in the meantime) and the promise that was being awaited resolves, then the async function is woken up and given the resolved value of the function to continue processing.
why not have the async function return the resolved promise value when used with await?
Because that would block the entire event loop, which is why asynchronous logic (starting with callback style APIs) was introduced into JS in the first place.
Otherwise async await seems a bit redundant.
Consider a situation where you want to request a number of URLs from an API serially (so that you don't shove too many simultaneous requests at the API):
const data = [];
for (let i = 0; i < urls.length; i++) {
const response = await fetch(urls[i]);
const response_data = await response.json();
data.push(response_data);
}
If you were to rewrite that without await then you'd probably end up with something recursive to count your way through the loop. It would be significantly more complex.
async/await makes dealing with complex combinations of promises simple, and simple promises trivial.
knew async/await was syntactical sugar to make async code look synchronous.
And it does that inside the async function and only inside it.
It doesn't really pause the function, it just allows you to write the rest of the function code as if it were. But it's really just wrapping the rest of the code of the function into a .then().
E.g.
await foo = someFunc();
console.log(foo);
is executed like
someFunc().then((foo => {
console.log(foo);
});
Since it's really still asynchronous, you can't return the resolved value, because the original calling function returns immediately. But if the calling function is also declared async, it returns a Promise, and the caller can either use await again (so it appears to be synchronous) or use .then().
I'm attempting to get a better grasp on async functions and promises in JS. To do this, I wrote a sample program that has the goal of calling a function that does busy work (purposefully not using async setTimeout as I want to mimic a long-running process) but returns immediately. However, I can't seem to figure out why this isn't working.
test();
async function intense(){
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}
async function test(){
console.log("Print 1");
intense(); // does some busy work for a few seconds
console.log("Print 2"); // want this to print immediately after print 1
}
When I run it, I get:
Print 1
Done with async work
Print 2
And I would like it to be:
Print 1
Print 2
Done with async work
I thought that it would print the latter sequence because I declared the function intense() to be async, so it would return a promise immediately and continue work asynchronously.
I even tried to refactor the intense function to be a promise that resolves immediately, but to no avail.
async function intense(){
return new Promise((resolve)=> {
resolve();
var start = new Date().getTime();
for (var i = 0; i < 1e6; i++) {
if ((new Date().getTime() - start) > 2000){
break;
}
}
console.log("Done with async work");
}, null)
}
What am I missing?
There a are a couple of reasons for what you're seeing:
An async function is synchronous up until its first await or return, so the entire function runs before returning in your case.
Busy-waiting isn't asynchronous.
test needs to use await if it's going to wait for intense to complete before continuing.
Moving something into a promise doesn't take it off the thread. The only way to do that in most JavaScript environments (including browsers) is to use a Worker thread (MDN, Node.js docs — Node.js has had Worker since ~v10.5, and while it's still marked "experimental" the main parts of the API should be fairly stable as they're drawn from the web worker standard API).
It's important to remember that promises don't make anything asynchronous¹, they provide a means of observing the result of something that's already asynchronous.
Here's an example using setTimeout for the asynchronous part that help you understand these better:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function intense(value) {
console.log("intense(" + value + ") - This is synchronous");
await delay(100);
console.log("intense(" + value + ") - This is asynchronous, because it's after `await`");
}
async function test(){
console.log("Print 1");
intense(1); // <== WITHOUT await
console.log("Print 2");
await intense(2); // <== With await
console.log("Print 3");
}
test();
.as-console-wrapper {
max-height: 100% !important;
}
¹ There's one small caveat to that: The handler you pass to then, catch, or finally will always be called asynchronously, even if the promise you're calling them on is already settled. That's literally the only thing that promises actually make asynchronous.
so it would return a promise immediately and continue work asynchronously.
No it would not. The callback passed to the Promise constructor is called immeadiately. What is async is the process of calling resolve or reject later on, and how .then chains get called back somewhen.
However it isnt async in the sense that the code runs on another thread or gets deferred, that won't happen as JS itself is executed in a single thread*.
console.log(1);
const promise = new Promise((resolve, reject) => {
console.log(2); // gets executed immeadiately
});
promise.then(() => console.log(4)); // < Promise resolve asynchronously
console.log(3);
*If you plan to do really "intense" work, it might be benefitial to do that in another thread (see WebWorker in browsers and child_process.spawn for NodeJS).
This is a misunderstanding that is pretty easy to make with javascript. Your function intense() blocks the thread. Putting something in an async function does not change the fact that you only have one thread in javascript. As soon as the interpreted starts running that for loop it's going to use the one thread to run it until it's over. Nothing else will happen until then.
Async functions don't return immediately, they run the body of the code until the hit an await and return a promise. In your example, the entire function will run before it returns.
You can't use this kind of long running process without blocking. That's why node goes out of its way to offload things like i/o access and timers to another thread.
See here for more details: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
If you want to run some long-running code like this asynchronously you will need to spawn a child process: https://nodejs.org/api/child_process.html
So basically I want to asynchronously execute 2 synchronous promises. Like so:
(function foo() {
var newValues = ...;
this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues));
});
})()
Now since I don't want to wait for saveMessages to finish execution I didn't add a .then() at the end. Since I don't care about the result. I just want to async function to execute so at some point we have the messages offline.
BUT I fear that the promise might get garbage collected since by the standard (as far as I know) you always need a .then for promises.
So my question is whether I need to add an empty .then to make sure it's not garbage collected too early and the function wouldn't execute correctly. Like so: this.saveMessages(msgs.concat(newValues)).then();?
And is it the same in the browser and NodeJS?
BUT I fear that the promise might get garbage collected since by the
standard (as far as I know) you always need a .then() for promises.
No. There does not exist any such standard -- to my knowledge -- which states that the GC will free up the memory allocated to the a Promise object if a .then() is not attached to it.
Promises will get executed regardless of whether a .then() -- or a .catch() for that matter -- is attached to it.
So my question is whether I need to add an empty .then() to make sure
it's not garbage collected too early and the function wouldn't execute
correctly.
No you do not need an empty .then()
And is it the same in the browser and NodeJS?
Yes, as long as you use same Promise implementation on both the runtimes.
So, you would be completely fine with this:
function foo() {
var newValues = ...;
this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues));
});
}
But, You should consider attaching a .catch() to it to handle any errors. If you don't, you will have UnhandledPromiseRejectionWarning. In future versions of Node, it can crash your whole app. Read about it here
EDIT
Even if the parent has nothing to process or execute, the program will wait till each of the 100 promises has resolved. Try this:
const n = 100;
const sleep = (id, seconds) =>
new Promise(resolve => {
setTimeout(() => {
console.log(`promise with id ${id} is resolving`);
resolve();
}, seconds * 1000);
});
for (let i = 0; i < n; i++) {
sleep(i, 1 * i);
}
console.log("Last line of parent");
No, the Promise knows nothing about how you're handling its completion until it gets there.
You don't need to worry about garbage collection preemptively, Javascript is smarter than that, but you do have to worry about the global execution context completing, as in, your program closing the connection, before the Promise has resolved, which would then cancel the promise and perform garbage collection.
Since you're running it asynchronously, if you want to guarantee the program awaits the completion of that Promise without making everything synchronous you can always hold the completion state of the Promises on a scoped variable or use an event emitter...
var imDone = false;
function foo() {
var newValues = ...;
this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues))
.catch(err=> { console.log(err); imDone = true; })
.then(()=> imDone = true);
}).catch(err=> console.log(err));
}
....
if (imDone) {
// finish execution, res.send() or res.end() in Node
}
you can use this code and return your first promise and second promise is run sync.
function foo() {
var newValues = ...;
return this.getMessages().then((msgs: Array<any>) => {
this.saveMessages(msgs.concat(newValues));
});
}
You are not executing the code in the right way. Promises should be handled in the right way.There is no issue with the garbage collector freeing up the space. Your code can be written in this manner which would increase the readability and make the code easier to understand.
// This is pseudo synchronous execution of the code
function foo() {
var newValues = 'testing';
return this.getMessages()
.then((msgs: Array<any>) => {
// here return is important
return this.saveMessages(msgs.concat(newValues));
});
}
You can reduce the complexity by using async/await feature:
aync function foo() {
try {
var newValues = 'testing';
const msgs = await this.getMessages()
await this.saveMessages(msgs.concat(newValues))
} catch (ex) {
// handle your error here
}
}
This question already has answers here:
How to synchronize a sequence of promises?
(6 answers)
Closed 5 years ago.
I have a promise chain problem I have been grappling with. I make a call out to an external api that returns me data that I need to process and ingest into a mongo database. I am using nodejs and mongodb with express. Anyway, the calls to the api are working ok, the problem is that I am making tons of them at once. I want to slow them down, like make all the calls for one set. Wait a minute. Make all the calls for the next set. If this was a known amount of sets I would just promise chain them. I dont know how many sets there are so I am looping through them. I think the closure is the problem but cant work around it. on to the example code!
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
// call out to api, get set of car data from factory1
console.log(carFactory);
if (true) {
console.log('resolved');
resolve("Stuff worked!"+carFactory);
}
else {
reject(Error("It broke"));
}
});
return promise;
}
function manager(){
//singular call
readApi("this is a singular test").then(returnedThing=>{
console.log(returnedThing); //Stuff worked! this is a singular test
});
let dynamicList = ["carFactory1", "carFactory2","carFactory3","carFactory..N"];
let readApiAction = [];
dynamicList.forEach(carIter=>{
readApiAction.push(readApi(carIter));
});
//ok so now I got an array of promise objects.
//I want to call the first one, wait 1 minute and then call the next one.
//originally I was calling promise.all, but there is no way to get at
//each promise to pause them out so this code is what I am looking to fix
let results= Promise.all(readApiAction);
results.then(data=>{
data.forEach(resolvedData=>{
console.log(resolvedData); //Stuff worked carFactory1, etc...
});
});
//singular call with timeout, this does not work, each one called at the same time
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
setTimeout(callingTimeout(carIter), 6000);
});
}
//just a function to call the promise in a timeout
//this fails with this - TypeError: "callback" argument must be a function
function callingTimeout(carIter){
readApi(carIter).then(returnedThing=>{
console.log("timeout version"+returnedThing);
});
}
A bit on theory. Native Promise.all just groups promises. They're still executed simultaneously (in async way, though, as all JS code, but along each other). This means that it will still congest the API and perform a lot of calls.
Another thing to note is that if you want to delay a promise, you have to delay its return value (e.g. resolve). In order to do so, you may use setTimeout INSIDE new Promise (just look below for more explanation).
Set timeout is asynchronous. It does not work as in other languages (it does not just pause execution). Setting fixed timeout in your code just caused to move ALL the executions by 6s. They still happened in parallel (in different ticks, though, but it's a small difference). Try e.g. generating different timeouts for each one in the loop - you'll notice that they're happening in a different time BUT! This is not a good solution for promisified code!
And now - time for practical answer!
If you use Bluebird, it has a special method to add delay or timeout to each promise. Without it, you would have to write a wrapper around Promise, e.g. resolving it after a particular amount of time and then use it with Promise.all.
First solution (bluebird):
function delayMyPromise(myPromise, myDelay);
return Promise.delay(myDelay).then(function() {
return myPromise;
});
});
and then in your code:
return Promise.all(delayMyPromise(promise1, 1000), delayMyPromise(promise2, 2000)); // Notice different delays, you may generate them programatically
Or even cooler, you can use Promise.map from Bluebird instead of Promise.all that has a special concurrency setting so you may force your promises to be executed in particular sequence, e.g. 2 at a time.
This is how I did it on my previous project :)
More here:
http://bluebirdjs.com/docs/api/promise.delay.html
http://bluebirdjs.com/docs/api/map.html
Pure native Promise implementation:
function delayMyPromise(myPromise, myDelay) {
return new Promise(function (resolve, reject) {
setTimeout(function() {
return resolve(myPromise);
}, myDelay);
});
}
I'd, however, heavily recommend first approach if you don't mind using Bluebird. It's like lodash for Promises and it's really fast :)
you get the error : TypeError: "callback" argument must be a function because your callingTimeout returns nothing and setTimeout needs a function as argument, this is how to fixe it :
let readApiActionTimeouts = [];
dynamicList.forEach(carIter=>{
callingTimeout(carIter)
});
your promise :
function readApi(carFactory){
var promise = new Promise(function(resolve, reject) {
//...
setTimeout(()=>{
resolve("Stuff worked!"+carFactory);
}, 6000);
//...
});
return promise;
}
You could use recursion for something like this.
When you call .forEach, each iteration happens immediately.
In the example below, doSomething is not called until the setTimeout occurs, which means each letter is printed 1 second apart.
let letters = ["a", "b", "c"];
function doSomething(arr) {
console.log(arr[0]);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
}
doSomething(letters);
Alternately, for your array of promises:
let promises = [
Promise.resolve("A"),
Promise.resolve("B"),
Promise.resolve("C"),
];
function doSomething(arr) {
arr[0].then((result) => {
console.log(result);
if (arr.length > 1) {
setTimeout(() => doSomething(arr.slice(1)), 1000);
}
})
}
doSomething(promises);