Is there a way to make the javascript await keyword work outside async functions? I would like to be able to freeze the entire call-stack (instead of just the rest of the async function), to be resumed once the particular promise returns a value. Sadly a powerful await like that is currently gimped or not implemented yet. I tried to make nodent.js work, but due to my custom loader and dynamic functions it's unfortunately impractical.
Is there a way to make the javascript await keyword work outside async functions?
Sadly, the answer is: No.
See the docs:
The await expression causes async function execution to pause, to wait for the Promise's resolution, and to resume the async function execution when the value is resolved. [emphasis added]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
I would like to be able to freeze the entire call-stack (instead of just the rest of the async function), to be resumed once the particular promise returns a value.
But how could your promise return a value if the entire call-stack was frozen? It would be stuck forever.
More details
To use await you need to be inside async function.
At least you have to do:
async function main() {
// use await here
}
main();
in your main code.
Or, using an IIFE - thanks to Robert Klep for the suggestion:
void async function main() {
// use await here
}();
Or, if you like punctuation:
(async () => {
// use await here
})();
Other options
There are some other options to handle concurrency in Node, like:
https://www.npmjs.com/package/co
https://www.npmjs.com/package/bluebird-co
https://www.npmjs.com/package/async-co
http://bluebirdjs.com/docs/api/promise.coroutine.html
http://taskjs.org/
none of which will freeze the call stack, however, as it would have the same effect as running kill -STOP with your own process ID and waiting for the stopped process to resume itself.
Given you are looking for a hack, not a proper promise-based concurrency solution, have a look at node-fibers (there are similar ones, but afaik this is the most popular, with multiple abstractions built around it). It does allow you to halt the current fiber in any synchronous function until something asynchronous happens that runs in a different fiber. Which of course is a horrible idea, but well…
Related
how can async/await be used when calling APIs where the data you're fetching is dependent on the rest of the function content under it?
What if the fake API calls here pushed more names to the array? The updateNames function would be called already before the api calls would be done. Wouldn't I want to block the runtime in this case and make it synchronous?
let names = [];
async foo() {
await Promise.all[
this.callAPI1();
this.callAPI2();
]
this.updateNames(names);
}
In your example, this.updateNames will be executed after your API calls are resolved because you have the await keyword and it is the same as doing:
Promise.all([call1(), call2()]).then(() => { this.updateNames() })
If you have any doubt you can always try by simulating an API call with a promisified setTimeout and some console.log:
const simulateAsync = () => {
return new Promise(resolve => {
setTimeout(() => {
console.log("resolved");
resolve();
}, 100);
});
};
async function test() {
await Promise.all([simulateAsync(), simulateAsync()]);
console.log("after");
}
test();
What if the fake API calls here pushed more names to the array? The updateNames function would be called already before the api calls would be done.
updateNames would not be called until both of the callAPI# functions completed; the await makes the code block (yielding control back to the event loop) until both promises (explicitly created or implicit when the functions are async themselves) complete.
If the functions in question don't return promises, executing synchronously/eagerly, then the await Promise.all([...]) is pointless (there were no promises involved), but there's still no risk of updateNames being called before the callAPI# calls finish; either:
callAPI# returns a promise (in which case await blocks the current task until they complete, returning control to the event loop while it waits), or
callAPI# does not return a promise and all the work is done immediately
and in either case, you can't get to updateNames before they finish.
Wouldn't I want to block the runtime in this case and make it synchronous?
You are blocking the current function call/task with the await. Other tasks scheduled on the event loop can run while the await is still blocked though, and that's the advantage to async processing. Every apparently asynchronous thing in JavaScript is sharing that event loop, and if you completely blocked (rather than awaiting an async call), none of them could run. This means, for example:
setTimeout/setInterval calls don't fire
UI drawing operations don't occur
User input is blocked
All events and observer handling code is deferred
Truly synchronous calls would make all of that (and more) wait until the entirety of foo completed. This is why async code is sometimes referred to as "cooperative multitasking"; control is never wrested away from the current task without its consent like you'd see in multithreaded code, instead control is handed back to the event loop voluntarily (via await) whenever a task is blocked waiting on asynchronous processing, or when the task completes. When the async task has a result, it waits for the event loop to hand control back to it, then continues where it left off.
This question already has answers here:
Correct way to write a non-blocking function in Node.js
(2 answers)
Closed 4 years ago.
I don't understand how to work with asynchronous functions.
Why does the code below stop the main thread?
async function foo() {
for (;;) {}
}
foo();
The async keyword, and promises in general, don't make synchronous code asynchronous, slow running code fast, or blocking code non-blocking.
async just makes the function return a promise and provides (with the await keyword) a mechanism to interact with other promises as if there were synchronous.
Your function starts a loop, and then just goes around and around.
It doesn't get to the end of the function, which would end the function and resolve the promise it returned.
It doesn't reach an await keyword and pause while it waits for the awaited promise to be resolved.
It just goes around and around.
If you were actually doing something in the loop which was computationally expensive and you wanted to push off into the background, then you could use a Node.js Worker Thread or a browser-based Web Worker to do it.
Putting the async keyword before the function only implies that this is asynchronous function. You need to include the keyword await before the function that you want to actually wait for. Just like this:
async function hashPin(pin){
const hashedPin = await bcrypt.hash(pin, saltRounds);
}
That's just the example from one of my projects (the redundant code has been removed before posting)
I have been using Promises and async/await, they are pretty much the same thing right? Usually what I would do is wrap my promise and return it etc.
function someFetchThatTakesTime(){
// Promisify the request.
return new Promise((resolve, reject) => {
if(allGood){
resolve();
}else{
reject();
});
}
Then I can do:
someFetchThatTakesTime()
.then(console.log('all good.')
.catch(console.log('some error occured.');
or I can do the:
async function wrapMyFetch() {
try {
// Make the async call.
data = await someFetchThatTakesTime();
return data;
} catch(err) {
// Propagate the exception up stream.
throw err;
}
}
(async () => {
let response = await wrapMyFetch();
// Do stuff with the response.
})();
I guess, pretty clear so far.
However, I have recently encountered situations where my application does not care about waiting for the results to be fetched and the data containers to be updated etc. Let's say there is one larger loop that runs infinitely and any Promised requests simply will fill-in the gaps as the application runs.
In that case, we don't really need the async/await pattern right? We just want to move forward with our loop and stones will fall in place behind us, whenever they are ready to fall in place (or not, in case of an error).
I would like to clarify this: async/await will just force things to run linearly, right? But if we do not want linearity, and we are ok with spawned tasks - Promises - to finish their thing in the background in some time when we are resuming with our run-cycle, we don't need that async/await pattern? Is that correct?
I have been using Promises and async/await, they are pretty much the same thing right?
Well, async/await are helpful syntax built on top of promises. Async/await relies on promises in order to work. So, I wouldn't quite call them the same thing, but you can code identically functioning code with await surrounded by try/catch and .then().catch(). You can use either. Both methods rely on promises.
However, I have recently encountered situations where my application does not care about waiting for the results to be fetched and the data containers to be updated etc. Let's say there is one larger loop that runs infinitely and any Promised requests simply will fill-in the gaps as the application runs.
In that case, we don't really need the async/await pattern right? We just want to move forward with our loop and stones will fall in place behind us, whenever they are ready to fall in place (or not, in case of an error).
There is no rule that the caller has to pay attention to a returned promise or has to wait for it before going on with their task. So, if you want to "just let the stones fall as they may in the background" as you say and that's appropriate for your application, you can code that way.
This is often referred to as "fire and forget" coding where you start some asynchronous operation that is going to do something on its own and you don't need to pay attention to when it finishes or if it had an error because that is of no consequence to the calling code.
But, there is a rule that you can't let a promise rejection go unhandled. So, if you're returning a promise and the caller isn't going to do anything with it, then you have to make sure that any rejections are handled by the operation itself (with .catch()) so that the only thing that ever gets returned back is a resolved promise. Even if all you do is notice the error and do nothing with it, you have to catch the error.
async/await will just force things to run linearly, right?
Not globally, no. await makes the code in the async function it's used in wait for the promise you pass it to settle, but that only affects the function you use it in, not anything calling that function.
async functions are syntactic sugar for writing a function that returns a promise. await is syntactic sugar for consuming promises. Together, they dramatically simplify using promises (in particular by making rejections errors that automatically propagate through the call tree).
More specifically, an async function runs its code synchronously until the first await or return (or until code runs off the end of the function), at which point it returns a promise. Its logic then waits for the promise to settle, and continues; eventually it settles its promise based on what happened to the last promise it awaited or returned (or fulfills it with undefined if code execution runs off the end).
If you don't want to wait for a promise to settle, you don't have to, not even in an async function. Just don't use await on it. For instance, assume we have this wrapper for fetch that gets JSON for us (and fixes the API footgun):
async function fetchJSON(...args) {
const response = await fetch(...args);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.json();
}
Here's an async function using it that does an initial query to get a list of things to fetch, and then fetches all of those things in parallel::
async function fetchList(listUrl) {
const list = await fetchJSON(listUrl);
return Promise.all(list.map(item => fetchJSON(itemUrl)));
}
Notice how fetchList uses await to wait for the list of things to fetch, but then doesn't wait for the items; it just returns the promise from Promise.all for the list of items it starts fetching.
Also note how await within fetchJSON makes fetchJSON's logic wait for the fetch promise to settle, but doesn't make the caller calling fetchJSON wait unless that caller uses await. fetchList only awaits the first call (to get the list), it doesn't wait for the item from the list.
Why should we have an async function in order to use await? Why can't we just use await without async? JS is async by default too, this just adds to the confusion.
Update:
I've seen some lads put my question on hold so I'll try to elaborate.
I'm just curious as to why this won't work:
some code
let users = await getUsers();
some code
Why should it be inside an async for it to work, ie
$(async function() {
some code
let users = await getUsers();
some code
});
JS is async by default too...
No, JavaScript is not async by default. The only async features of JavaScript are fairly newly-added:
Promise resolution
async/await
JavaScript is commonly used in environments where you interact with asynchronous things (like event handlers in the DOM, or I/O completions in Node.js), but JavaScript is not asynchronous (other than above).
In the words of Allen Wirfs-Brock, who was the editor of the ECMAScript specification for many years, JavaScript...
(has) an observably synchronous execution model. Other than via Atomics/SABs there are no observable shared-state race conditions.
Back to your question:
Why should we have an async function in order to use await?
Before too long, with modules you won't have to, once the top level await proposal finishes working through the process. It just got to Stage 3.
But the answer is that await is syntactic sugar for consuming a promise, and one of the rules of promises is that you either handle errors or return the chain to the caller (so it can handle errors or return the chain to its caller). await doesn't handle errors, so it has to return the chain to the caller. The way it does that is that an async function always returns a promise, and that promise is chained to the promise await awaits.
That is, this:
async function foo() {
const thingy = await somethingAsyncReturningAPromise();
return thingy.foo;
}
is conceptually (but not literally) this:
function foo() {
return somethingAsyncReturningAPromise()
.then(thingy => thingy.foo);
}
If something goes wrong in somethingAsyncReturningAPromise, the promise returned by foo rejects — the error is propagated to the caller.
As far as I can tell from the top-level await proposal, it simply allows unhandled rejections at the top level of the module to be unhandled rejections. So just like this code causes an unhandled error:
null.doSomething();
this code in an async module would cause an unhandled rejection:
await somethingThatReturnsAPromiseAndRejects();
Why should we have an async function in order to use await? Why can't we just use await without async?
Because async/await is "just" syntactic sugar for Promises. If the function is async, then it returns a Promise. It is not possible to have the "await" behaviour without returning a promise. The fact that the function is async has to be marked explicitly.
JS is async by default too, this just adds to the confusion.
This statement is too "simplified". While it is true that JS is async in nature, because of the event loop, this doesn't mean that every function has an async behavior. This does not add to the confusion. You're probably confused due to misunderstanding how JS really works. You should read about Promises, which are behind the scenes when you see async/await.
JavaScript has task based concurrency. It basically means that code blocks (tasks) runs synchronously without being interrupted, until it reaches a certain breakpoint (the end of a task). That has some advantages:
1) You do have concurrency, e.g. a network call does not block your script from doing other things in the meantime (so the task that consumes the network request will only run if the network request was done, other tasks can be done in the meantime).
2) On the other hand, you do not have concurrent mutations, which eliminates a lot of problems (let a = 1; a += 1; could evaluate to 3, you would need locks / semaphores to prevent those, c.f. Java & others).
Now async / await allow you to define such tasks:
An async function can be divided into tasks, await serves as a breakpoint:
let a = 1;
async function stuff() {
a = a + 1; // this is totally secure, as no other code might run in the meantime
a = a + await other(); // this is unsafe, as we await, which means that other tasks might be executed in the meantime.
}
If you want to "await in non async functions" that basically means that you won't know wether a certain function call runs synchronously (meaning: without other code running in the meantime) or not:
function dangerous() { await stuff(); }
let a = 1;
a = a + dangerous(); // does that work or not? Can a be manipulated in the meantime?
So with your proposal you would basically remove the borders around tasks, every code might run every inbetween. So at the end that causes chaos, and chaos is not good if you want to be productive.
So I tried doing something like this
async function() {
while (x) {
}
// code
}
The reason I want to do this is because I need something to be done only when X becomes false, and I want it to be in another thread so my website is still usable, and according to the naming and what I read about it, async function should be async... It's only natural right?
But instead, my application is completely frozen when the while loop is running. Can someone please explain why this happens and if there's a way around it?
Thank you.
async does not mean multithreading, it just means that you can use the await keyword it it and that the value returned by that function is guaranteed to be a Promise, that its result will always be resolve/rejected in an asynchronous way.
So if the code within the async function does not utilizes any other asynchronous code then it would block the rest of the code the same way as a regular function would do.
If you use await within your while loop, then other code that is waiting could interleave at that point.
async function() {
while (x) {
// ...
await someFunction() // at this `await` other code could be executed
// ...
}
// code
}
So a await in the async function does two things, if the right side of the await is a Promise, then it will wait until it is resolve (or rejected) and it will allow other code that is scheduled to be executed to interleave.
I just wanted to share how I fixed my issue. It might not be the cleanest solution or the right solution, but it did just what I wanted with no performance issues.
All I wanted is to make a while loop that doesn't interrupt my code and at least seems like it runs asynchronously... and the async keyword wasn't what I was hoping it was, and doesn't do anything close to it. Instead, I tried this solution and it worked perfectly.
setTimeout(function() {
while (true) {
if (x) {
break;
}
}
// code
}, 0);
It seems that when you type 0 for the delay in a setTimeout, it executes the function you pass to it only after all of the pending code has executed. And so, it makes it act like it's kind of an async function. It accomplished what I wanted and works seamlessly, so that's what's improtant. If there's a better simple solution, please inform me.
Thank you.