async函数
-
async是什么?它和Genernator函数外观上的区别是什么?
const asyncReadFile = async function(){ const f1 = await readFile(url1) const f2 = await readFile(url2) }
async是Genernator函数的语法糖。
async
和Genernator
的区别有两点:-
Genernator
的*
变成async
-
yield
变成await
-
-
async
对Genernator
做了哪些改进?- 内置执行器。
执行时和普通函数一样,只需要调用函数就完了,不需要
next()
方法 - 更好的语义。
async
表示它后面的函数里有异步操作;await
表示紧跟在后面的表达式需要等待结果 - 更广的适用性。
await后面可以
Genernator
,Promise对象
和原始类型的值。(原始类型的值会被自动转化为resolved状态的Promise对象) - 返回值是Promise
async
函数返回的是Promise
对象,方便用then
进行下一步操作 - 总的来说:
async
函数可看成多个异步操作,包装成一个Promise
对象,而await
命令就是内部then
命令的语法糖。
- 内置执行器。
-
async
执行的例子:function timeout(ms){ return new Promise((resolve) => { setTimeout(resolve, ms) }) } async function asyncPrint(value, ms){ await timeout(ms); console.log(value) } asyncPrint("你好啊", 5000)
5s之后打印 “你好啊”
以上
asyncPrint
的意思是,等待timeout函数执行完了之后,才会继续执行console.log(value)
-
async函数的几种使用场景?
五种场景下:
-
函数声明
async function foo(){}
-
函数表达式
const foo = async function(){}
-
箭头函数
const foo = async () => {}
-
对象的方法
let obj = { async foo(){ } } obj.foo().then(...)
-
class方法
class Storage{ constructor(){ this.cachePromise = caches.open("avatars") } async getAvatar(name){ const cache = await this.cachePromise; return cache.match() } } const storage = new Storage() storage.getAvatar("joy").then(...)
-
- async函数返回什么?
返回一个
Promise
对象。async
函数内部 return语句的返回值,会成为then
方法回调函数的参数。async
函数内部抛出的错误,会导致返回的Promise
对象变为rejected
状态 -
`async
函数中Promise
对象是如何变化的?async
函数返回的是Promise
对象P。必须等到内部await
命令的Promise
对象执行完后,P才会发生状态改变,除非遇到return语句,或者抛出了错误。换言之,
async
函数 内部的异步操作执行完了,才会执行调用它时后面then
方法指定的回调函数。 -
await
命令的返回值是什么?- 正常情况下,
await
后面是Promise
对象,会返回此对象的结果;如果不是Promise
对象,直接返回对应的值 -
await
命令后面跟了thenable
对象,会把thenable
对象当做Promise
对象来处理
- 正常情况下,
-
await
后面的Promise
对象如果变为rejected
会怎样?-
rejected的参数会被
async
函数catch方法的回调函数接收到。async function f() { await Promise.reject('出错了'); } f() .then(v => console.log(v)) .catch(e => console.log(e))
-
任何一个
await
语句的Promise
对象变为reject状态,整个async
函数都会被中断async function f() { await Promise.reject('出错了'); await Promise.resolve('hello world'); // 不会执行 }
-
-
如果希望前一个异步操作失败,不中断后面的异步操作,怎么处理?
两种方法:
-
把第一个
await
放到try...catch
里面async function f(){ try{ await Promise.reject("出错了") }catch(e){} return await Promise.resolve("hello world") } f().then(v =>console.log(v)).catch(e => console.log(e)) // 打印的是 hello world
-
await
后面的Promise
对象再跟一个catch
方法,处理前面可能出现的错误async function f(){ await Promise.reject("出错啦").catch(e => { console.log("await 内部promise被reject了") }) return await Promise.resolve("hello world"); } f().then(v => console.log(v)).catch(e => console.log(e))
打印的内容是:
await 内部promise被reject了
hello world
-
-
await后面的异步操作出错了(例如某行代码throw 了一个Error),是什么意思?
async
函数返回的Promise
对象被reject
了async function f() { await new Promise(function (resolve, reject) { throw new Error('出错了'); }); } f() .then(v => console.log(v)) .catch(e => console.log(e)) // catch执行了, e就是抛出的错误对象 new Error('出错了')
-
如何防止出错呢?
还是将其放到
try{ }catch(e){ }
代码块中。如果有多个
await
命令,可以将其统一放到try{ }catch(e){ }
结构里。
下面是实际例子: 多次发起客户端请求,如果请求成功,跳出循环往下执行;如果不成功,继续请求,直到达到最大数目 NUM_RETRIESasync function test(){ let i; for(i = 0; i < NUM_RETRIES; ++i){ try{ await superagent.get(url) break; }catch(err){} } console.log(i) } test()
如果
await
操作成功,则会break,跳出for循环;如果await
操作不成功,则会被catch住,然后继续下一轮for循环,直到超过 NUM_RETRIES或者await
操作成功。 -
async
和await
有哪些使用上注意的点?-
await
命令后的Promise
对象可能reject
,因此await
命令最好放在try{ }catch(e){ }
代码块中async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } } // 另一种写法 async function myFunction() { await somethingThatReturnsAPromise() .catch(function (err) { console.log(err); }); }
-
多个
await
异步操作时,如果不存在继发关系,让它们同时触发比较好可以结合
Promise.all
方法// 写法一 let [foo, bar] = await Promise.all([getFoo(), getBar()]); // 写法二 let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;
-
await
命令只能用在async
函数中,用在普通函数里会报错注意
forEach
的回调函数,await
也不能出现在回调函数里 -
async
函数可以保留运行堆栈看例子:
const a = () => { b().then(() => c()) ; } const a = async () => { await b(); c(); }
上面的例子中,b运行时,a可能已经执行完了。如果此时b或c报错,错误堆栈将不包括a
下面例子中,b运行时,a只是暂停,若此时b或者c报错了,错误堆栈中将包括a
-
-
与
Promise
写法和Genernator
写法,async
有什么好处?Promise
写法有很多catch
和then
,语义性不强。Genernator
函数需要有一个任务运行器,自动执行Genernator
函数,并且yield
后面的表达式,必须返回Promise
对象async
最简洁,最符合语义,将Genernator
写法的自动执行器,改在 语言层面提供,不暴露给用户,代码量最少。async function chainAnimationAsync(elem, ainmations){ let ret = null try{ for(letanim of animations){ ret = await anim(elem); } }catch(e){} return ret }
-
async
的实例: 按顺序完成异步操作——依次远程读取一组URL,然后按照读取顺序输出结果async function logInOrder(urls){ const textPromises = urls.map(async url => { const response = await fetch(url) return response.text() }) for(const textPromise of textPromises){ console.log(await textPromise) } }
map的参数是
async
函数。这几个async
是并发的。只有async
函数内部才是继发的【const response = await fetch(url)
比return response.text()
先执行】,外部并不受影响。后面在
for...of
循环内部使用了await
,这几个await
是顺序执行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。