含义 async函数可以让异步操作更方便,它是Generator函数的语法糖.
使用Generator函数读取两个文件的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const fs = require ('fs' );function readFile (fileName ){ return new Promise ((resolve, reject ) => { fs.readFile (fileName, (err, data ) => { if (err) { return reject (err) } resolve (data) }) }) } var gen = function * () { var f1 = yield readFile (fileName1) var f2 = yield readFile (fileName2) console .log (f1.toString ()) console .log (f2.toString ()) }
使用async函数:
1 2 3 4 5 6 var asyncReadfile = async function ( ) { var f1 = await readFile (fileName1) var f2 = await readFile (fileName2) console .log (f1.toString ()) console .log (f2.toString ()) }
可以看出,aysnc函数就是把Generator函数的yield换成await,*换成async
Generator函数的执行要靠执行器,await函数自带执行器,它的执行与普通的函数一样.
async函数返回值是Promise
用法 async函数返回一个Promise,可以使用then方法添加回调函数,函数执行的时候一旦遇到await就会先返回,等异步操作完成,在执行后面的语句
一个例子,指定多少毫秒后输出一个值:
1 2 3 4 5 6 7 8 9 10 function timeout (ms ) { return new Promise ((resolve, reject ) => { setTimeout (resolve, ms) }) } async function asyncPrint (value, ms ) { await timeout (ms); console .log (value) } asyncPrint ('hello world' , 50 )
语法 返回Promise async函数返回一个Promise,会成为then方法调用的参数
1 2 3 4 async function f ( ) { return 'hello world' } f ().then (res => console .log (res))
async函数内部抛出的错误会导致Promise对象变成reject状态,抛出的错误会被catch方法捕获
1 2 3 4 async function f ( ) { throw new Error ('Error!' ) } f ().catch (err => console .log (err))
await命令 正常来说,await命令跟随一个Promise,如果不是,就会被转成一个立即resolve的Promise
1 2 3 4 async function f ( ) { return await 123 } f ().then (res => console .log (res))
await命令后面的Promise如果变为reject状态,会被catch方法捕获
1 2 3 4 async function f ( ) { Promise .reject ('Error' ) } f ().catch (err => console .log (err))
只要一个await语句后面的Promise变成了reject,整个函数都会停止执行
如果想要函数继续执行,可以将await放在try…catch语句里面
1 2 3 4 5 6 7 8 async function f ( ) { try { await Promise .reject (new Error ('Error!' )) } catch (e) { } return await Promise .resolve ('Success!' ) } f ().then (res => console .log (res))
另一种方法是在await后面的Promise添加catch方法
1 2 3 4 5 6 7 async function f ( ) { await Promise .reject (new Error ('Error!' )).catch (err => { console .log (err) }) return await Promise .resolve ('Success!' ) } f ().then (res => console .log (res))
使用注意 最好把await命令放在try…catch中
如果两个await命令后面的异步操作不存在激发关系,最好让他们同时触发 ,例如:
1 2 let foo = await getFoo ()let bar = await getBar ()
这两个异步操作是相互独立的,又被写成了继发关系,所以比较耗时.可以让它们同时触发
1 2 let [foo,bar]=await Promise .all ([getFoo (),getBar ()])
1 2 3 4 5 let fooPromise = getFoo ()let barPromise = getBar ()let foo = await fooPromiselet bar = await barPromise
await只能用在async函数中 ,如果用在普通函数内会报错,async函数中其他函数的回调函数也不能用await
对一个数组遍历进行异步操作,正确的写法是采用for循环,而不是forEach
1 2 3 4 5 6 7 async function dbFun ( ) { let docs = [{}, {}, {}] for (let doc of docs) { await db.post (doc) } }
如果需要多个请求并发进行,使用Promise.all方法
1 2 3 4 5 6 async function dbFun ( ) { let docs = [{}, {}, {}] let promises = docs.map (doc => dbFun.post ) let res = await Promise .all (promises) console .log (res) }
或者使用这个写法:
1 2 3 4 5 6 7 8 9 10 async function dbFun ( ) { let docs = [{}, {}, {}] let promises = docs.map ((doc ) => db.post (doc)) let res = [] for (const promise of promises) { res.push (await promise) } console .log (res) }
async函数的实现原理 async函数的实现原理就是将Generator函数和自动执行器包在一个函数里
1 2 3 4 5 6 7 8 9 10 11 async function fn (args ) { } function fn (args ) { return spawn (function * () { }) }
spawn函数就是自动执行器
按顺序完成异步操作 依次远程读取一组url,然后按照读取的顺序输出结果:
Promise写法:
1 2 3 4 5 6 7 8 9 function logInOrder ( ) { const textPromises = urls.map (url => { return fetch (url).then (res => res.text ()) }) textPromises.reduce ((chain, textPromise ) => { return chain.then (() => textPromise).then (text => console .log (text)) },Promise .resolve ()) }
这种写法不美观,可读性差,用async函数实现:
1 2 3 4 5 6 async function logInOrder ( ) { for (const url of urls) { const res = await fetch (url) console .log (await res.text ()) } }
虽然代码被简化,但是这些操作都是继发的,非常浪费时间,可以将它改成并发的
1 2 3 4 5 6 7 8 9 async function logInOrder ( ) { const textPromises = urls.map (async url => { const res = await fetch (url) return res.text () }) for (const textPromise of textPromises) { console .log (await textPromise) } }