async是什么?它和Genernator函数外观上的区别是什么?javascript
const asyncReadFile = async function(){ const f1 = await readFile(url1) const f2 = await readFile(url2) }
async是Genernator函数的语法糖。java
async
和Genernator
的区别有两点:promise
Genernator
的 *
变成async
yield
变成 await
async
对Genernator
作了哪些改进?并发
执行时和普通函数同样,只须要调用函数就完了,不须要 next()
方法异步
async
表示它后面的函数里有异步操做;await
表示紧跟在后面的表达式须要等待结果async
await后面能够Genernator
, Promise对象
和原始类型的值。(原始类型的值会被自动转化为resolved状态的Promise对象)函数
async
函数返回的是Promise
对象,方便用 then
进行下一步操做fetch
async
函数可当作多个异步操做,包装成一个Promise
对象,而await
命令就是内部then
命令的语法糖。async
执行的例子:this
function timeout(ms){ return new Promise((resolve) => { setTimeout(resolve, ms) }) } async function asyncPrint(value, ms){ await timeout(ms); console.log(value) } asyncPrint("你好啊", 5000)
5s以后打印 “你好啊” url
以上 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(...)
返回一个 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_RETRIES
async 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
是顺序执行。