I am new to node js and trying to understand how to make async calls.
I am trying to wrap a function into a promise in order to make it async. For simplicity, the function is just a loop that takes time :
var p = new Promise(resolve => {
for (let i=0;i<999999999;i++){}
resolve('foo')
});
p.then(function (value) { console.log(value);});
console.log('bar');
I am expecting to see :
bar // printed immediately
foo // printed after the loop finishes
Instead, they are both printed after the loop completion.
How to make the loop block run asynchronously?
Ps. Apologies for the indentation/formatting, I had to write this from a phone.
Thanks
You seem to assume that "asynchronous code" involves concurrency, in the sense that code will run at the same time in another thread. That is not the case. Unless you start another thread by purpose, JS itself will run in a single thread. Therefore no matter what you do with promises: Either the loop runs first and then the logs run or the logs run first and then the loop runs.
You could also achieve concurrent execution through multitasking: If you stop the loop inbetween, other code can run in the meantime:
(async function() {
while(true) {
console.log("loop");
await Promise.resolve();
}
})();
console.log("in the meantime");
But there's nothing asynchronous about your promise. Creating a promise starts the execution of the function, and JS always runs to completion.
Typically, a promise is used to initiate something asynchronous, like an API call, or even a plain setTimeout, that runs outside JS's thread. Your code, however, will iterate through the empty loop bajillion times, and only after that any following lines will be run.
Replace the empty line with a timeout, and it will become async:
var p = new Promise(resolve => {
setTimeout(() => resolve("foo"), 2000);
});
p.then(function(value) {
console.log(value);
});
console.log('bar');
Related
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
I know JS is single threaded. But I have a function which takes time for the calculation. I would like it to work paralleled, so this function would not freeze next statement. Calculation inside of function will take approximately 1-2 seconds.
I used to create it using promise, but it still freeze the next statement.
console.log("start");
new Promise((res, rej) => {
/* calculations */
}).then((res) => console.log(res));
console.log("end");
Then I used setTimeout function with time interval 0. LoL
console.log("start");
setTimeout(() => {
/* calculations */
console.log(res);
}, 0);
console.log("end");
Outputs:
start
end
"calculation result"
Both cases shows similar result, but using promise prevented to show console.log("end") before calculation finishes. Using setTimeout works as I wanted, and shows console.log("end") before calculation, so it was not freeze till calculation done.
I hope it was clear enough. Now for me using setTimeout is the best solution, but I would be happy to hear your ideas or any other method calculating concurrently without setTimeout.
The code you write under new Promise(() => {..code here..}) is not asynchronous. The is a very common misconception that everything under the Promise block would run asynchronously.
Instead, this JS API just let's get us a hook of some deferred task to be done once the promise is resolved. MDN
Promises are a comparatively new feature of the JavaScript language that allow you to defer further actions until after a previous action
has completed, or respond to its failure. This is useful for setting
up a sequence of async operations to work correctly.
new Promise(() => {
// whatever I write here is synchromous
// like console.log, function call, setTimeout()/fetch()/async web apis
// if there are some async tasks like fetch, setTimeout.
// they can be async by themselves but their invocation is still sync
//
})
setTimeout is not the correct option either. Code under setTimeout would run when the event stack is empty and once it enters, it would block the main thread again.
The right approach to this would be to use Web Workers.
When a promise is resolved, it resumes code execution after any awaits in the next task.
So for instance:
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
}
foo();
console.log("end");
prints "start", "end", "foo". Even though the promise is already resolved, it still waits for any other code to execute first, and only after that it resumes and prints foo.
I would like to execute any awaits synchronously for resolved promises.
The only way I can think of to achieve this is to check if a promise has been fulfilled and wrap the await in an if statement so that it only waits if the promise is still pending. But figuring out if a promise is pending (and getting the resolved value) seems very tedious to do synchronously. Does anyone know if there's a better way?
More context
I have a game loop that needs to not run for more than a few milliseconds per frame. There is one specific function that I need to call in order to prepare some gltf assets. This function takes more than a second. So I'd like to devide this up into chunks, so it excecutes only part of this function every frame.
The easiest way to achieve this that I could think of was to make the function async and checking if a certain amount of time has passed. If more than a few milliseconds have passed, I will await waitForFrameRender(), which will essentially stop the execution until the next frame. However, this will add tons of awaits in the function (it has a bunch of loops), and most of them won't really have to wait for anything because the function hasn't been running for more than X amount of milliseconds yet.
Therefore it seemed to make more sense to skip these waits and run these parts synchronously.
In my current setup I have an if statement to check how much time has passed, and only await if it has actually been running for more than a few milliseconds. Which works to a certain degree, but the function that takes a second to execute also has nested functions that are async for the same reason. So the nested function calls still need an await.
I realize that this sounds like web workers are the solution. I haven't actually tried this yet but I feel like transferring the javascript object that the function returns will have too much overhead for this. And since it is ok for this function to not return immediately (even 20 seconds or so would be fine) simply making the function async seemed like the easiest way to do it.
If this is what you want move end into the promise. The other alternative would be to put end in its own promise
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
console.log("end");
}
foo();
or
console.log("start");
async function foo(){
await Promise.resolve();
console.log("foo");
}
foo();
await Promise.resolve();
console.log("end");
This is probably what you want. You need to await until foo() is done running.
async function run() {
console.log("start");
await foo();
console.log("end");
}
run();
I have a browser application where I want to change some text in the UI to say that the page is loading, then run a long process, and once the process is complete to say that the page is finished loading.
Using the code written below I can get this to work when I call ProperlyUpdatesUIAsync, where the text is changed while the long process is running, and then once the long process is complete, it changes again to indicate that it is done.
However, when I use the DoesNotUpdateUIUntilEndAsync method, the UI does not get updated until after the long process is finished, never showing the "loading" message.
Am I misunderstanding how async/await works with JavaScript? Why does it work in the one case but not in the other?
async function ProperlyUpdatesUIAsync(numberOfImagesToLoad) {
$("#PageStatusLabel").text("Loading..");
await pauseExecutionAsync(2000);
$("#PageStatusLabel").text("Loaded");
}
// this method doesn't do anything other than wait for the specified
// time before allowing execution to continue
async function pauseExecutionAsync(timeToWaitMilliseconds) {
return new Promise(resolve => {
window.setTimeout(() => {
resolve(null);
}, timeToWaitMilliseconds);
});
}
async function DoesNotUpdateUIUntilEndAsync(numberOfImagesToLoad) {
$("#PageStatusLabel").text("Loading..");
await runLongProcessAsync();
$("#PageStatusLabel").text("Loaded");
}
async function runLongProcessAsync() {
// there is a for loop in here that takes a really long time
}
Edit:
I experimented with a few things and this new refactor is giving me the desired result, but I do not like it. I wrapped the long running loop in a setTimeout with a timeout setting of 10. With a value of 10, the UI is updated before running the loop. However, a value of 0 or even 1 does not allow the UI to update, and it continues to behave as if the timeout was not declared at all. 10 seems so arbitrary. Can I really rely on that working in every scenario? Shouldn't async/await defer execution until the UI is updated without my having to wrap everything in a timeout?
async function runLongProcessThatDoesNotBlockUIAsync() {
return new Promise(resolve => {
window.setTimeout(() => {
// there is a for loop in here that takes a really long time
resolve(null);
}, 10);
});
}
EDITED
The code in runLongProcessAsync() never yeilds/surrenders the thread for updates to take place.
try: -
<!DOCTYPE html>
<html>
<script type="text/javascript">
var keep;
async function DoesNotUpdateUIUntilEndAsync(numberOfImagesToLoad) {
document.getElementById("PageStatusLabel").innerHTML="Loading..";
p = new Promise((resolve) => {keep = resolve})
setTimeout(theRest,0); //let the Loading message appear
return p;
}
async function theRest(){
await runLongProcessAsync(); // Your await here is useless!
document.getElementById("PageStatusLabel").innerHTML="Loaded";
keep();
}
async function runLongProcessAsync() {
// there is a for loop in here that takes a really long time
for (var x=1; x<1000000000;x++){b=x^2}
}
</script>
<body onload="DoesNotUpdateUIUntilEndAsync(5)">
<p>Test</p>
<p id="PageStatusLabel"></p>
</body>
</html>
I'm not sure what you are attempting but my guess is you want Web Worker to give you another thread. Either that or you don't understand that "await" just gets rid of the need for callbacks. If your code is purely synchronous simply labelling "async" does nothing.
I have a node application that is not a web application - it completes a series of asynchronous tasks before returning 1. Immediately before returning, the results of the program are printed to the console.
How do I make sure all the asynchronous work is completed before returning? I was able to achieve something similar to this in a web application by making sure all tasks we completed before calling res.end(), but I haven't any equivalent for a final 'event' to call before letting a script return.
See below for my (broken) function currently, attempting to wait until callStack is empty. I just discovered that this is a kind of nonsensical approach because node waits for processHub to complete before entering any of the asynchronous functions called in processObjWithRef.
function processHub(hubFileContents){
var callStack = [];
var myNewObj = {};
processObjWithRef(samplePayload, myNewObj, callStack);
while(callStack.length>0){
//do nothing
}
return 1
}
Note: I have tried many times previously to achieve this kind of behavior with libraries like async (see my related question at How can I make this call to request in nodejs synchronous?) so please take the answer and comments there into account before suggesting any answers based on 'just use asynch'.
You cannot wait for an asynchronous event before returning--that's the definition of asynchronous! Trying to force Node into this programming style will only cause you pain. A naive example would be to check periodically to see if callstack is empty.
var callstack = [...];
function processHub(contents) {
doSomethingAsync(..., callstack);
}
// check every second to see if callstack is empty
var interval = setInterval(function() {
if (callstack.length == 0) {
clearInterval(interval);
doSomething()
}
}, 1000);
Instead, the usual way to do async stuff in Node is to implement a callback to your function.
function processHub(hubFileContents, callback){
var callStack = [];
var myNewObj = {};
processObjWithRef(samplePayload, myNewObj, callStack, function() {
if (callStack.length == 0) {
callback(some_results);
}
});
}
If you really want to return something, check out promises; they are guaranteed to emit an event either immediately or at some point in the future when they are resolved.
function processHub(hubFileContents){
var callStack = [];
var myNewObj = {};
var promise = new Promise();
// assuming processObjWithRef takes a callback
processObjWithRef(samplePayload, myNewObj, callStack, function() {
if (callStack.length == 0) {
promise.resolve(some_results);
}
});
return promise;
}
processHubPromise = processHub(...);
processHubPromise.then(function(result) {
// do something with 'result' when complete
});
The problem is with your design of the function. You want to return a synchronous result from a list of tasks that are executed asynchronously.
You should implement your function with an extra parameter that will be the callback where you would put the result (in this case, 1) for some consumer to do something with it.
Also you need to have a callback parameter in your inner function, otherwise you won't know when it ends. If this last thing is not possible, then you should do some kind of polling (using setInterval perhaps) to test when the callStack array is populated.
Remember, in Javascript you should never ever do a busy wait. That will lock your program entirely as it runs on a single process.
deasync is desinged to address your problem exactly. Just replace
while(callStack.length>0){
//do nothing
}
with
require('deasync').loopWhile(function(){return callStack.length>0;});
The problem is that node.js is single-threaded, which means that if one function runs, nothing else runs (event-loop) until that function has returned. So you can not block a function to make it return after async stuff is done.
You could, for example, set up a counter variable that counts started async tasks and decrement that counter using a callback function (that gets called after the task has finished) from your async code.
Node.js runs on A SINGLE threaded event loop and leverages asynchronous calls for doing various things, like I/O operations.
if you need to wait for a number of asynchronous operations to finish before executing additional code
you can try using Async -
Node.js Async Tutorial
You'll need to start designing and thinking asynchronously, which can take a little while to get used to at first. This is a simple example of how you would tackle something like "returning" after a function call.
function doStuff(param, cb) {
//do something
var newData = param;
//"return"
cb(newData);
}
doStuff({some:data}, function(myNewData) {
//you're done with doStuff in here
});
There's also a lot of helpful utility functions in the async library available on npm.