本文最后更新于:8 个月前
async是asynchronous的缩写,意为异步。sync是synchronous的缩写,意为同步 async函数的返回值是什么?async/await组合的使用方式,await放在不同位置时代码的执行顺序有什么区别?await一定会阻塞代码执行吗?
JavaScript异步(三)async与await async 和 await 其实就是 Generator 和 Promise 的语法糖。async 函数和普通 函数没有什么不同,他只是表示这个函数里有异步操作的方法,并返回一个 Promise 对象
1 2 3 4 5 6 7 8 9 10 async function async1 ( ) { console .log ("async1 start" ); await async2 (); console .log ("async1 end" ); }async function async1 ( ) { console .log ("async1 start" ); Promise .resolve (async2 ()).then (() => console .log ("async1 end" )); }
一、async 1.1、async函数 async作为一个关键字,放在函数前面,表示该函数是一个异步函数。这是aysnc关键字的用途。
async 函数是使用async
关键字声明的函数 。async 函数是 AsyncFunction
构造函数的实例,并且其中允许使用 await
关键字。async
和 await
关键字让我们可以用一种更简洁的方式写出基于 Promise
的异步行为,而无需刻意地链式调用 promise
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function resolveAfter2Seconds ( ) { return new Promise (resolve => { setTimeout (() => { resolve ('resolved' ); }, 2000 ); }); }async function asyncCall ( ) { const result = await resolveAfter2Seconds () console .log (result) console .log ('calling' ) }asyncCall ()resolveAfter2Seconds ().then (res => { console .log (res) })console .log ('calling' )
1.1、async函数返回值 async 函数的返回值是一个 promise 对象 。
async可以指定返回一个Promise对象。但是如果 async 函数没有指定返回的Promise对象,JavaScript也会隐式地用Promise.resolve方法将函数返回值包装成一个Promise对象
1 2 3 4 5 async function asyncFn ( ){ return 'hi' }var res = asyncFn () res
1 2 3 4 5 6 async function asyncFn (param ){ console .log (param) }var res = asyncFn ('hi' ) res
1.3、async解决“回调地狱” npm提供了async包,它包含了series
、parallel
等方法。async - npm (npmjs.com)
场景示例:先后读取两个文本文件,然后按顺序显示
使用async.series()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var fs = require ('fs' );var async = require ('async' );async .series ([ function (cb ){ fs.readFile ('a-text-file.txt' ,'utf8' ,cb); }, function (cb ){ fs.readFile ('another-text-file.txt' ,'utf8' ,cb); } ],function (err,value ){ if (err) console .error (err); else { console .log ('File 1:' ,values[0 ]); console .log ('File 2:' ,values[1 ]); } });
再来看看仅仅使用回调的代码,与上面代码比较,此处代码的可读性差,串行读取效率低。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var fs = require ('fs' ); fs.readFile ( 'a-text-file.txt' , 'utf8' , function (err,text ) { if (err) { console .error (err); } else { console .log ('First text file:' ,text); fs.readFile ( 'another-text-file.txt' , 'utf8' , function (err,text ) { if (err) { console .error (err); } else { console .log ('Second text file:' ,text); } } ); } } );
二、await关键字 await 关键字只能放到 async 函数里面
2.1、await表达式 await后面可以是Promise实例,异步函数、thenable、普通表达式
2.1.1 await+promise对象 await+promise对象表达式,会暂停整个async函数的执行进程并让出进程控制权,只有当其等待的基于Promise的异步操作(这一点很重要!) 被resolve或reject后才恢复async函数的进程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function doubleAfter2seconds (num ){ return new Promise ((resolve,reject )=> { setTimeout (()=> { resolve (2 * num) },2000 ); }) }async function testResult ( ){ var result= await doubleAfter2seconds (10 ); console .log (result); console .log ('hi' ) }testResult ()
2.1.2 await+普通表达式 await后面接普通表达式的时候,并不会起到阻塞代码的效果!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function resolveAfter2Seconds ( ) { setTimeout (() => { console .log ('resolved' ); }, 2000 ) }async function asyncCall ( ) { await resolveAfter2Seconds () console .log ('calling' ); }asyncCall ()
2.1.3 await+异步函数 1 2 3 4 5 6 7 8 9 10 11 12 async function resolveAfter2Seconds ( ) { setTimeout (() => { console .log ('resolved' ); }, 2000 ) }async function asyncCall ( ) { await resolveAfter2Seconds () console .log ('calling' ); }asyncCall ()
虽然resolveAfter2Seconds被async关键字声明为了异步函数,但是await resolveAfter2Seconds()并没有起到阻塞同步代码的作用!
2.2、「try-catch」捕获错误 使用了async+await,就不需要再用Promise.then().catch()的处理方式,但同时也意味着,无法再使用catch(()=>{})的方式捕获错误。不过这里可以使用「try-catch」的方式捕获错误。
1 2 3 4 5 6 7 8 9 async function testResult ( ){ try { let result1 = await doubleAfter2seconds (10 ) let result2= await doubleAfter2seconds (20 ) console .log (result1+result2) }catch (err){ console .error (err); } }
需要注意,这里的catch()
与Promise.catch()
的形式是有所区别的。两者比较如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async function testResult ( ){ try { let result1 = await doubleAfter2seconds (10 ) let result2= await doubleAfter2seconds (20 ) console .log (result1+result2) }catch (err){ console .error (err); } }var p = new Promise (()=> {...}) p.then ((res )=> { ... }).catch ((err )=> { ... })
2.3、await 和 并行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 function resolveAfter2Seconds ( ) { console .log ("starting slow promise" ); return new Promise ((resolve ) => { setTimeout (() => { resolve ("slow" ); console .log ("slow promise is done" ); }, 2000 ); }); }function resolveAfter1Second ( ) { console .log ("starting fast promise" ); return new Promise ((resolve ) => { setTimeout (() => { resolve ("fast" ); console .log ("fast promise is done" ); }, 1000 ); }); }async function sequentialStart ( ) { console .log ("==SEQUENTIAL START==" ); const slow = await resolveAfter2Seconds (); console .log (slow); const fast = await resolveAfter1Second (); console .log (fast); }async function concurrentStart ( ) { console .log ("==CONCURRENT START with await==" ); const slow = resolveAfter2Seconds (); const fast = resolveAfter1Second (); console .log (await slow); console .log (await fast); }function concurrentPromise ( ) { console .log ("==CONCURRENT START with Promise.all==" ); return Promise .all ([resolveAfter2Seconds (), resolveAfter1Second ()]).then ( (messages ) => { console .log (messages[0 ]); console .log (messages[1 ]); } ); }async function parallel ( ) { console .log ("==PARALLEL with await Promise.all==" ); await Promise .all ([ (async () => console .log (await resolveAfter2Seconds ()))(), (async () => console .log (await resolveAfter1Second ()))(), ]); }sequentialStart (); concurrentStart () concurrentPromise () parallel ()