MENU
async Function
The development of asynchronous execution in JavaScript culminated in the introduction of async functions in ECMAScript-8. Implicitly returning a promise, async functions combine the characteristics of generators and Promise, with 'await' behaving similarly to 'yield'.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
function sleep(duration){ return new Promise(resolve=>{ setTimeout(resolve, duration); });}async function start_demo(){ sleep(1000).then(()=>{ alert("with promise. before. 1 level of indentation."); }); await sleep(5000); alert("with async. now. no indentation.")}start_demo();
</script></body><html>
RESETRUNFULL
<!DOCTYPE html><html><body><script>
async function wait3s(){ return await new Promise(resolve=>{ setTimeout(resolve,3000,"done"); });}console.log(wait3s());// won't work...Promise {<pending>}(async ()=>{console.log(await wait3s());})(); // IIFE, will work...donewait3s().then(console.log);// will work…done
</script></body><html>
There are a few different ways to define async functions, but all require the 'async' keyword.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
async function f(){};const f = async function(){};const f = async =>{};const f = async x=>{};const f = async (x,y)=>{};let o = { async f(){} };async * ag(){}; // async generator functions are not supported by all, as of July 2017
</script></body><html>
'await' should be followed by a Promise.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
async function af1(){
for (let i=0; i<3; i++)
await (new Promise(
(resolve,reject)=>{console.log(1);resolve();}))
.then(()=>{console.log(2);});}async function af2(){
for (let i=0; i<3; i++)
await (new Promise(
(resolve,reject)=>{console.log(3);resolve();})).then(()=>{console.log(4);});}af1();af2();// 1 3 2 4 1 3 2 4 1 3 2 4// without ‘async’ and ‘await’: 1 1 1 3 3 3 2 2 2 4 4 4
</script></body><html>
Here is what happens every time when 'await' is encountered within an async function during execution:
step 1: The callback (in the immediate Promise) is executed, as part of the current macrotask.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
{console.log(1); resolve();} // executed immediately
</script></body><html>
step 2: Next, the current execution context is added to the microtask queue (job queue) along with the result obtained in step 1. The execution context queued points to either the next single then(), or, (when there is no more then()) what is following 'await'.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
{console.log(2); } // pushed to microtasks queue without running
</script></body><html>
step 3: The execution then jumps out of the async function and continues from where the async function was called in the first place.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
af2(); // executed as in step 1 and step 2
</script></body><html>
step 4: When the macrotask ends (ie. when the call stack becomes empty), all the microtasks in the microtask queue are executed sequentially, adding more microtasks to the microtask queue if necessary, until the queue becomes empty.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
{console.log(2);} // first task in queue executed
</script></body><html>
step 5 : Eventually, a macrotasks queue is picked again. The oldest macrotask is retrieved from it and executed. Steps 1-5 are repeated.
Without 'async' and 'await', the execution continues without leaving the async functionin step 3.
The async function, which is an AsyncFunction object, when called, implicitly returns a Promise. Each block of code representing an entire promise or each of the callbacks in '.then()' is atomic, in the sense that its execution is not overlapped.Below is a more complex example, which completely explains the execution order.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
setTimeout(()=>console.log('#'));async function a(){
setTimeout(()=>console.log('@'));
for (let i=0; i<10; i++)
console.log(await (new Promise(resolve=>{
for (let j=0; j<=2; j++) console.log(j);
resolve(1000);
console.log('+');
})).then(()=>{
for (let j=0; j<=2; j++) console.log(j+'a');
return 20000;
}).then(()=>{
for (let j=0; j<=2; j++) console.log(j+'aa');
return 50000;}));
return 'aaa';}async function b(){
setTimeout(()=>console.log('$'));
for (let i=0; i<10; i++)
console.log(await (new Promise(resolve=>{
for (let j=5; j<=7; j++) console.log(j);
resolve(3000);
console.log('*');
})).then(()=>{
for (let j=5; j<=7; j+=1) console.log(j+'b');
return 30000;
}).then(()=>{
for (let j=5; j<=7; j+=1) console.log(j+'bb');
return 70000;
}));
return 'bbb';}a().then(x=>{console.log(x)})
.then(x=>{console.log('aEND');});console.log('XXX');b().then(x=>{console.log(x)})
.then(x=>{console.log('bEND');});console.log('YYY');/* 0 1 2 + XXX 5 6 7 * YYY 0a 1a 2a 5b 6b 7b 0aa 1aa 2aa 5bb 6bb 7bb 500000 1 2 + 70000 5 6 7 * 0a 1a 2a 5b 6b 7b 0aa 1aa 2aa 5bb 6bb 7bb 500000 1 2 + 70000 5 6 7 * 0a 1a 2a 5b 6b 7b 0aa 1aa 2aa 5bb 6bb 7bb 500000 1 2 + 70000 5 6 7 * 0a 1a 2a 5b 6b 7b 0aa 1aa 2aa 5bb 6bb 7bb 50000...(repetitions)70000 aaa bbb aEND bEND # @ $*/
</script></body><html>
To catch errors of async functions, append it with a .catch() clause or enclose the call within try...catch blocks.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
function divide(n){ return new Promise((resolve,reject)=>{ let d = prompt("Divide 100 by :"); if (d==0) reject("Division by zero."); else
resolve(n/d); });}async function start_demo(){ alert(await divide(100).catch(alert)); // on input 5: 20 // on input 0: Division by zero. --> undefined try{ await divide(100); } catch (e){ alert(e); }}start_demo();
</script></body><html>
As of July 2017, errors thrown after an 'await' statement in a try...catch block in an async function are silently swallowed.
RESETRUNFULL
<!DOCTYPE html><html><body><script>
(async function() {
try {
await new Promise(x=>x);
throw 'y';
} catch(e) {
console.log(e);
}})(); // (no output)
</script></body><html>