This draft deletes the entire topic.
Examples
-
A function defined as
async
is a function that can perform asynchronous actions but still look synchronous. The way it's done is using theawait
keyword to pause the function while it waits for a Promise to resolve/reject.Note: Async functions are a Stage 4 ("Finished") proposal on track to be included in the ECMAScript 2017 standard.
async function getJSON(url) { const response = await fetch(url); return await response.json(); }
An async function always returns a Promise itself, so you can use it in other asynchronous functions.
-
You have to keep the operator precedence in mind when using
await
keyword.Imagine that we have an asynchronous function
getUnicorn()
which returns a Promise that resolves to an instance of classUnicorn
. Now we want to get the size of the unicorn using thegetSize()
method of that class.Look at the following code:
await getUnicorn().getSize()
At first sight it looks valid, but actually it's not. Due to operator precedence, it's equivalent to the following:
await (getUnicorn().getSize())
Here we attempt to call
getSize()
method of the Promise object, which isn't what we want.Instead, we should use brackets to denote that we first want to wait for the unicorn, and then call
getSize()
method of the result:(await getUnicorn()).getSize()
Of course the previous version could be valid in some cases, for example if the
getUnicorn()
function was synchronous, but thegetSize()
method was asynchronous. -
-
Improvements requested:
-
Other: The rewritten example it will not work because `.json()` returns a promise and it will return an error when you will do response.success ([See the documentation](https://developer.mozilla.org/en-US/docs/Web/API/Body/json)) – Andrici Cezar Oct 6 at 14:58add a comment
Consider the following function using promises:
function newUnicorn() { return fetch('unicorn.json') // fetch unicorn.json from server .then(response => response.json()) // parse the response as JSON .then(unicorn => fetch('new/unicorn', { // send a request to 'new/unicorn' method: 'post', // using the POST method body: JSON.stringify({unicorn}) // pass the unicorn to the request body }) ) .then(response => response.json()) .then(response => response.success) // return success property of response .catch(err => console.log('Error creating unicorn:', err)); }
The function can be rewritten using
async
/await
as follows:async function newUnicorn() { try { const unicorn = (await fetch('unicorn.json')).json() const response = ( await fetch('new/unicorn', { method: 'post', body: JSON.stringify({unicorn}) }) ).json() return response.success } catch (err) { console.log('Error creating unicorn:', err); } }
Basically, the
await
keyword allows you to use asynchronous functions (that is, functions that return promises) as if they were synchronous. Note that you can only useawait
inasync
functions. You can also use anasync
IIFE if you want to execute that code immediately:(async () => { await makeCoffee() console.log('coffee is ready!') })()
-
-
When using async await in loops, you might encounter some of these problems.
If you just try to use await inside
forEach
, this will throw anUnexpected token
error.(async() => { data = [1, 2, 3, 4, 5]; data.forEach(e => { const i = await somePromiseFn(e); console.log(i); }); })();
This comes from the fact that you've erroneously seen the arrow function as a block. The
await
will be in the context of the callback function, which is notasync
.
The interpreter protects us from making the above error, but if you addasync
to theforEach
callback no errors get thrown. You might think this solves the problem, but it won't work as expected.Example:
(async() => { data = [1, 2, 3, 4, 5]; data.forEach(async(e) => { const i = await somePromiseFn(e); console.log(i); }); console.log('this will print first'); })();
This happens because the callback async function can only pause itself, not the parent async function.
You could write an asyncForEach function that returns a promise and then you could something like
await asyncForEach(async (e) => await somePromiseFn(e), data )
Basically you return a promise that resolves when all the callbacks are awaited and done. But there are better ways of doing this, and that is to just use a loop.You can use a
for-of
loop or afor/while
loop, it doesn't really matter which one you pick.(async() => { data = [1, 2, 3, 4, 5]; for (let e of data) { const i = await somePromiseFn(e); console.log(i); } console.log('this will print last'); })();
But there's another catch. This solution will wait for each call to
somePromiseFn
to complete before iterating over the next one.
This is great if you actually want yoursomePromiseFn
invocations to be executed in order but if you want them to run concurrently, you will need toawait
onPromise.all
.(async() => { data = [1, 2, 3, 4, 5]; const p = await Promise.all(data.map(async(e) => await somePromiseFn(e))); console.log(...p); })();
Promise.all
receives an array of promises as its only parameter and returns a promise. When all of the promises in the array are resolved, the returned promise is also resolved. Weawait
on that promise and when it's resolved all our values are available.The above examples are fully runnable. The
somePromiseFn
function can be made as an async echo function with a timeout. You can try out the examples in the babel-repl with at least thestage-3
preset and look at the output.function somePromiseFn(n) { return new Promise((res, rej) => { setTimeout(() => res(n), 250); }); }
-
With promises:
function doTheThing() { return doOneThing() .then(doAnother) .then(doSomeMore) .catch(handleErrors) }
With async functions:
async function doTheThing() { try { const one = await doOneThing(); const another = await doAnother(one); return await doSomeMore(another); } catch (err) { handleErrors(err); } }
Note how the return is at the bottom, and not at the top, and you use the language's native error-handling mechanics (
try/catch
). -
Often you will want to perform asynchronous operations in parallel. There is direct syntax that supports this in the
async
/await
proposal, but sinceawait
will wait for a promise, you can wrap multiple promises together inPromise.all
to wait for them:// Not in parallel async function getFriendPosts(user) { friendIds = await db.get("friends", {user}, {id: 1}); friendPosts = []; for (let id in friendIds) { friendPosts = friendPosts.concat( await db.get("posts", {user: id}) ); } // etc. }
This will do each query to get each friend's posts serially, but they can be done simultaneously:
// In parallel async function getFriendPosts(user) { friendIds = await.db.get("friends", {user}, {id: 1}); friendPosts = await Promise.all( friendIds.map(id => db.get("posts", {user: id}) ); // etc. }
This will loop over the list of IDs to create an array of promises.
await
will wait for all promises to be complete.Promise.all
combines them into a single promise, but they are done in parallel.
Remarks
Async functions are a sugar over promises. They help you make your code more readable and more linear-looking, with less levels of indentation. They do not, however, replace the Promise type, and the lowest level function still probably need to be written with the Promise constructor.
This is not yet a definitive feature of the language, it is currently in stage 3 (candidate) status , this means:
The proposal is mostly finished and now needs feedback from implementations and users to progress further.
Topic Outline
- Await and operator precedence
- Introduction
- Async functions compared to Promises
- Looping with async await
- Less indentation
- Simultaneous async (parallel) operations
Syntax
Sign up or log in
Save edit as a guest
Join Stack Overflow
Using Google
Using Facebook
Using Email and Password
We recognize you from another Stack Exchange Network site!
Join and Save Draft