Why do I need to await an async function when it is not supposedly returning a Promise?
All async
functions return a promise. All of them.
That promise will eventually resolve with whatever value you return from the async
function.
await
only blocks execution internal to the async
function. It does not block anything outside of the function. Conceptually, an async
function starts to execute and as soon as it hits an await
instruction, it immediately returns an unfulfilled promise (in the pending state) from the function and the outside execution world gets that promise and continues to execute.
Sometime later, the internal promise that was being await
ed will resolve and then the execution of the rest of the internals of the function will continue. Eventually the internals of the function will finish and return a value. That will trigger resolving the promise that was returned from the function with that return value.
FYI, there's a lot of superfluous stuff in your load()
function. You can change it from this:
async function load() {
const data = await new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
console.log(`Data inside the function: ${JSON.stringify(data)}`);
return data;
}
to this:function load() {
return new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
}
Then, use it like this:load().then(result => {
console.log(result);
});
Or, I prefer to encapsulate the manual creation of promise in their own function like this:function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
function load() {
return delay(10, [1, 2, 3]).then(data => data.map(i => i * 10));
}
And, it turns out this little delay()
function is generally useful in lots of places where you want to delay a promise chain.Thanks to everyone participating and providing me with insight. But I'm still confused how should I be usingFirst off, most of the time you only mark a functionawait
andasync
.
async
if you need to use await
inside the function.Second, you most commonly use await
(from within an async
function) when you have multiple asynchronous operations and you want to sequence them - often because the first one provides a result that is used as input to the second. You can use await
when all you have is a single asynchronous operation, but it doesn't really offer much of an advantage over a simple .then()
.
Here are a few examples of good reasons to use async
/await
:
Sequencing multiple asynchronous operations
Imagine you have getFromDatabase()
, getTheUrl()
and getTheContent()
that are all asynchronous. If any fails, you would want to just reject the returned promise with the first error.
Here's how this looks without async
/await
:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
Here's how this looks with async
/await
:async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
In both cases, the function returns a promise that resolves with the finalValue
so these two implementations are used the same by the caller:run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
But, you will notice that the async
/await
implementation has more of a serialized, synchronous look to it and looks more like non-asynchronous code. Many find this easier to write, easier to read and easier to maintain. The more processing you have between steps, including branching, the more advantages accrue to the async
/await
version.Automatically catching both rejected promises and synchronous exceptions
As I said earlier, async
functions always return a promise. They also have to built-in error handling that automatically propagates errors back to that returned promise.
It goes without saying that if you manually return a promise from the async
function and that promise rejects, then the promise returned from the async
function will reject.
But also, if you are using await
and any promise you are awaiting rejects and you don't have a .catch()
on the promise and don't have a try
/catch
around it, then the promise the function returns will automatically reject. So, back in our previous example of this:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
If any of the three promises that are being awaited rejects, then the function will short circuit (stop executing any more code in the function) and reject the async
returned promise. So, you get this form of error handling for free.Then lastly, an async
function also catches synchronous exceptions for you and turns them into a rejected promise.
In a normal function that returns a promise such as we had earlier:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
If getFromDatabase()
throws a synchronous exception (perhaps triggered because someArg
is invalid), then this overall function run()
will throw synchronously. That means that for the caller to catch all possible errors from run()
, they have to both surround it with a try
/catch
to catch the synchronous exceptions and use a .catch()
to catch the rejected promise:try {
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
} catch(e) {
console.log(err);
}
This is messy and a bit repetitive. But, when run()
is declared async
, then it will NEVER throw synchronously because any synchronous exception is automatically converted to a rejected promise so you can be sure you are capturing all possible errors when it's written this way:async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
// will catch all possible errors from run()
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
Should I always call all of my function with an await
?
First, you would only ever use await
with a function that returns a promise as await
offers no usefulness if the function does not return a promise (just adding to the clutter of your code if not needed).Second, whether you use await
or not depends upon the context of both the calling function (since you HAVE to be in an async
function to use await
and on the flow of logic and whether it benefits from using await
or not.
Places where it's pointless to use await
async function getKey(someArg) {
let key = await getFromDatabase(someArg);
return key;
}
The await
here isn't doing anything useful. You're not sequencing multiple async
operations and you're not doing any processing on the return value. You can accomplish the exact same code by just returning the promise directly:async function getKey(someArg) {
return getFromDatabase(someArg);
}
And, if you know that getFromDatabase()
never throws synchronously, you can even remove the async
from the declaration:function getKey(someArg) {
return getFromDatabase(someArg);
}
Let's say I'm writing a code composed of multiple functions within multiple files. If I end up using a library which returns a Promise or it's anThis is a bit too general of an ask that's hard to answer in a general case. Here are some thoughts along this general direction:async
function, should I trace back all my function calls from the asynchronous point to the entry point of the application and add anawait
before all the function calls after making themasync
?
Once any part of your result that you're trying to return from your function
A()
is asynchronous or uses any asynchronous operation to obtain, the function itself is asynchronous. In plain Javascript, you can never return an asynchronous result synchronously so your function must use an asynchronous method to return the result (promise, callback, event, etc...).Any function
B()
that calls your asynchronous functionA()
that is also trying to return a result based on what it gets fromA()
is now also asynchronous and also must communicate its result back using an asynchronous mechanism. This is true for a functionC()
that callsB()
and needs to communicate back its result to the caller. So, you can say that asynchronous behavior is infectious. Until you reach some point in the call chain where you no longer need to communicate back a result, everything has to use asynchronous mechanisms to communicate the result, error and completion.There's no specific need to mark a function
async
unless you specifically need one of the benefits of anasync
function such as the ability to useawait
inside that function or the automatic error handling it provides. You can write functions that returning promises just fine without usingasync
on the function declaration. So, "NO" I don't go back up the call chain making everythingasync
. I only make a functionasync
if there's a specific reason to do so. Usually that reason is that I want to useawait
inside the function, but there is also the automatic catching of synchronous exceptions that get turned into promise rejections that I described earlier. You would not generally need that with well behaved code, but it is sometimes useful with poorly behaved code orcode with an undefined behavior.await
is also only used when there's a specific reason for it. I don't just automatically use it on every function that returns a promise. I've described above reasons to use it. One can still use.then()
just fine for processing the result from a single function call that returns a promise. In some cases, it's just a matter of personal style whether you want to use.then()
orawait
and there is no particular reason it has to be one one way or the other.
Or maybe I should just get into the habit of calling all my functions with anAbsolutely NOT! First off, the last thing you want to do is to take perfectly synchronous code and unnecessarily make it asynchronous or even make it look asynchronous. asynchronous code (even withawait
regardless of whether they areasync
or not?
async
and await
) is more complicated to write, debug, understand and maintain than synchronous code so you would never want to unnecessarily make synchronous code into asynchronous code by adding async
/await
into it:For example, you would never do this:
async function random(min, max) {
let r = await Math.random();
return Math.floor((r * (max - min)) + min);
}
First off, this is a perfectly synchronous operation that can be coded like this:function random(min, max) {
let r = Math.random();
return Math.floor((r * (max - min)) + min);
}
Second off, that first async
implementation has made the function a lot hard to use as it now has an asynchronous result:random(1,10).then(r => {
console.log(r);
});
Instead of just the simple synchronous use:console.log(random(1,10));
Why doesn't .then() need the async keyword when used (similar to await)? How does Javascript know it's an asynchronous operation?
The purpose of async
/await
is to allow writing async code in a serial manner, which is mentally simpler to reason about (for some human-beings). This is useful, if you need to wait for async operation to finish before you continue with the rest of the code. For example, if you need to pass result of async operation as parameter.
Example 1
function asyncOperation1(n) { return Promise.resolve(n+1); }
function asyncOperation2(n) { return Promise.resolve(n/2); }
function asyncOperation3(n) { return Promise.resolve(n*3); }
function errorHandler(err) { console.error(err); }
function main() {
// flow-control
asyncOperation1(1)
.then(asyncOperation2)
.then(asyncOperation3)
.then(continueAfterAsync)
.catch(errorHandler)
// function wrapper
function continueAfterAsync(result) {
console.log(result);
}
}
main();
With async
/await
the code of the main
function above may look likeasync main() {
try {
console.log(
await asyncOperation3(
await asyncOperation2(
await asyncOperation1(1)
)
)
);
} catch(err) {
errorHandler(err);
}
}
Pay attention that we don't need to rewrite async operation functions to be async function asyncOperation...
to use await
, but we need to declare main function as async main
.Which one is better(?) is the mater of developers's taste and previous programming languages experience. The benefit that I can see is that you don't need to wrap everything into functions and introduce additional flow-control code, leaving this complexity to JavaScript compiler.
However, there are cases, when you want to schedule some parallel tasks and you don't care which one will finish first. These kind of things would be relatively hard to do with async
/await
only.
Example 2
function main() {
Promise
.all(
['srv1', 'srv2', 'srv3'].map(
srv => fetch(`${srv}.test.com/status`)
)
])
.then(
responses => responses.some(res => res.status !== 200) ?
console.error('some servers have problems') :
console.log('everything is fine')
)
.catch(err => console.error('some servers are not reachable', err))
}
So, we see that there is a room for both .then()
and await
to coexist.In some cases function may be either synchronous or asynchronous, depending on business logic (I know it's ugly, but in some cases it's unavoidable). And here we come to your main question
why don't we need to mark an asynchronous operation with .then() and we have to do it with awaitIn other words, why do we need
async
keyword at all?Example 3
// without `async`
function checkStatus(srv) {
if (!srv.startsWith('srv')) {
throw new Error('An argument passed to checkStatus should start with "srv"')
}
return fetch(`https://${srv}.test.com/status`);
}
function main() {
// this code will print message
checkStatus('srv1')
.then(res => console.log(`Status is ${res.status === 200 ? 'ok': 'error'}`))
.catch(err => console.error(err));
// this code will fail with
// Uncaught TypeError: (intermediate value).then is not a function
checkStatus('svr1')
.then(res => console.log(`Status is ${res.status === 200 ? 'ok': 'error'}`))
.catch(err => console.error(err));
}
However, if we define async function checkStatus
, compiler will wrap the runtime error into rejected promise return value, and both parts of the main
function will work.Now let's imagine that JavaScript allows to write functions that use await
without specifying async
in front of them.
Example 4 (not a valid Javascript)
function checkStatus(srv) {
if (cache[srv]) {
data = cache[srv];
} else {
data = (await fetch(`https://${srv}.test.com/status`)).json();
}
data.x.y = 'y';
return data;
}
What would you expect checkStatus
to return? Promise, raw value or throw exception (in case data.x
is undefined)?If you say Promise, then it would be hard for developer that uses this function to understand why inside of checkStatus
one can write data.x
and outside of it (await data).x
is required.
If raw value, the whole execution flow becomes cumbersome, and you can no longer rely on the fact that JavaScript is a single-threaded language, where no-one can change the value of the variable between two lines of code that are written in serial manner.
As you noticed, async
/await
is a syntactic sugar. If this syntax allows me to avoid possible runtime errors at earlier stage and keep the language backward compatible, I'm eager to pay the price of putting extra async
in front of async functions.
Also, I would recommend to read the answers to JS async/await - why does await need async?
Why does an async function finishes executing after a promise started later?
You're surprised why async1 end
comes after promise2
and promise3
, although it was called before them, and microtasks are executed in the order they are enqueued.
However, it really boils down to how many microtasks does it take for the async
function to resolve.
Take a look at this (it's the same code but with 4 raw promises):
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2 start');
return new Promise((resolve, reject) => {
resolve();
console.log('async2 promise');
})
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
}).then(function() {
console.log('promise4');
});
console.log('script end');
/* Just to make the console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
async/await logs to the console but returns no value and returns `Promise { pending }`
I'm simplifying quite a lot here, but it helps to understand. When you declare a function as async
, what it does is wrap the function in a Promise. So, for example, this function :
async function something(){ return "hello world" }
can be read like :function something(){
return new Promise((resolve, reject) => {
resolve("hello world")
})
}
In a nutshell, a Promise just schedules the execution of a function (in this case something
) for later. So, we can only read the returned value later, when it's ready. When you call something()
, it returns a Promise which is pending because it hasn't been executed yet (its execution is put on hold in a queue). You can learn more about how this works by reading about the event loop, the call stack and event queue.To get the returned value of a Promise, we use a method called .then((value) => {})
which returns in the variable value
the returned value of the function (in our case, value will contain "hello world"). So :
let r = something()
console.log(r) // Promise pending
r.then((value) => {
console.log(value) // "hello world"
})
If you want to use the value
, you are gonna have to either :- Put your code inside the
.then()
and continue there - Put your code inside an async function and use
await
in front of the call
async function run(){
let value = await something()
console.log(value) // "hello world"
// do things with value
}
run()
Incorrect value after calling promise async function in js
Try this:
onDeviceReady.then(async function () {
await howManyRecords();
console.log("after calling function records are: " + records);}
Promise Stays Pending, Returns Null
First off, ALL async
functions return a promise. The return value in the async function becomes the resolved value of that promise. So, the caller of an async
function MUST use .then()
or await to get the resolved value from the async function. There is no way to "circumvent" the asynchronicity like you are attempting. You can tame it to make it more usable, but you can't escape it. So, your async
function returns a pending promise that will eventually resolve to whatever value you return inside your async
function.
You can read more about how async
functions work here in this other answer.
In trying to make a minimal, reproducible example of your code, I've reduced it to this where I've substituted an asynchronous simulation for the database call:
function delay(t, v) {
return new Promise(resolve => setTimeout(resolve, t, v));
}
// simulate asynchronous database operation
const db = {
shift: {
findUnique: function(data) {
return delay(100, { id: 123, date: Date.now(), allDevices: ["iPhone", "Galaxy", "Razr"] });
}
}
}
const findShift = async (date) => {
console.log("In mutation function")
const found = await db.shift.findUnique({
where: {
date: date
}
})
return found;
}
const date = Date.now();
const foundShift = findShift(date).then(resolved => {
console.log("printing resolved...")
console.log(resolved);
if (resolved.id != 'undefined') {
console.log({
id: resolved.id,
date: resolved.date,
allDevices: resolved.allDevices
})
return foundShift
} else {
throw new Error("no shift of that date found!")
}
});
When I run this in nodejs, I get this error:[TypeError: Chaining cycle detected for promise #<Promise>]
And, the error is caused by this line of code:return foundShift
You are attempting to return a promise that's already part of this promise chain from within the promise chain. That creates a circular dependency which is not allowed.What you need to return there is whatever you want the resolved value of the parent promise to be. Since that looks like it's the object you construct right above it, I've modified the code to do that. This code can be run and foundShift
is a promise that resolves to your object.
function delay(t, v) {
return new Promise(resolve => setTimeout(resolve, t, v));
}
// simulate asynchronous database operation
const db = {
shift: {
findUnique: function(data) {
return delay(100, { id: 123, date: Date.now(), allDevices: ["iPhone", "Galaxy", "Razr"] });
}
}
}
const findShift = async (date) => {
const found = await db.shift.findUnique({
where: {
date: date
}
})
return found;
}
const date = Date.now();
const foundShift = findShift(date).then(resolved => {
if (resolved.id != 'undefined') {
let result = {
id: resolved.id,
date: resolved.date,
allDevices: resolved.allDevices
};
return result;
} else {
throw new Error("no shift of that date found!")
}
});
// foundShift here is a promise
// to get it's value, you have to use .then() or await on it
foundShift.then(result => {
console.log("final result", result);
}).catch(e => {
console.log(e);
});
Related Topics
Create Copy of Multi-Dimensional Array, Not Reference - JavaScript
Why Do I Need to Copy an Array to Use a Method on It
Fix the Upstream Dependency Conflict Installing Npm Packages
Using Filesystem in Node.Js with Async/Await
Event.Wheeldelta Returns Undefined
Return Index of Greatest Value in an Array
JavaScript Strings Outside of the Bmp
How to Concatenate Regex Literals in JavaScript
Updating Svg Element Z-Index with D3
How Does Appcelerator Titanium Mobile Work
Efficient Way to Insert a Number into a Sorted Array of Numbers
How to Load All Files in a Directory Using Webpack Without Require Statements
Jquery Smooth Scroll to an Anchor
Splicing a JavaScript Array from Within the Callback Passed to Foreach
How to Bind to the Change Event of a Textarea in Jquery
How to Sort an Associative Array by Its Values in JavaScript