async函数,使得异步变得更方便。async函数是什么?一句话就是 Generator的语法糖。javascript
用 Generator 依次读取两个文件java
var fs = require('fs')
var readFile = functiton(filename) {
return new Promise(function (resolve,reject) {
fs.readFile(fileName, function(error, data) {
if(error) return reject(error)
resolve(data)
})
})
}
var gen = function* (){
var f1 = yield readFile('/1')
var f2 = yield readFile('/2')
console.log(f1.toString())
console.log(f2.toString())
}
复制代码
将上面写成 async 函数promise
var asyncReadFile = async function(){
var f1 = await readFile('/1')
var f2 = await readFile('/2')
console.log(f1.toString())
console.log(f2.toString())
}
复制代码
经过比较,async 函数就是将 Generator 函数的 * 替换成了 async ,将yield 题替换成 await。仅此而已。并发
async 函数 返回一个Promise 对象,可使用then添加回调,当函数执行的时候,一旦遇到 await 就会先返回,等到异步完成,再接着执行函数体后面的语句。异步
指定多少毫秒输出一个值async
function timeout(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms)
})
}
async function asyncPrint(value,ms) {
await timeout(ms)
cosnole.log(value)
}
asyncPrint('hello world',50)
复制代码
写成这种方式也能够函数
async function timeout(ms){
await new Promise(resolve =>{
setTimeout(resolve,ms)
})
}
async function asyncPrint(value,ms){
await timeout(ms)
console.log(value)
}
复制代码
//函数声明
async function foo(){}
// 函数表达式
const foo = async function(){}
// 对象方法
let obj = {async foo() {}}
obj.foo.then()
// class 方法
class Storage {
constructor(){
this.catchPromise = caches.open('avatars')
}
async getAvatar(name) {
cosnt catch = await this.catchPromise;
return catch.match(`/avatars/${name}.jpg`)
}
}
const storage = new Storage()
storage.getAvatar('jack').then(...)
复制代码
async 函数 返回一个 Promise 对象。fetch
async 内部 return 语句返回的值,会成为 then 方法回调的参数动画
async function f(){
return 'hello world'
}
f().then(v=>{
console.log(v) // hello world
})
复制代码
async 函数内部抛出错误会致使返回的Promise对象变为 rejected 状态。抛出的错误对象 会被 catch方法回调函数接收到。ui
async 函数返回Promise对象必须等到内部全部的 await 命令后边Promise 对象执行完才会发生状态改变,除非遇到 return 或者 抛出错误。只有 async 函数内部全部的异步操做执行完,才会执行 then 方法指定的回调函数。
正常状况下, await 以后应该是一个 Promise 对象。若是不是,会被转化为一个当即resolve 的Promise。
async function f(){
return await 123;
}
f().then(v => console.log(v)) //123
复制代码
await 后面的Promise 变为 rejected,reject参数会被catch到
async function f(){
await Promise.reject('error')
}
f()
.then(v => console.log(v))
.catch(e => console.log(e)) // error
复制代码
只要有一个await语句后面的Promise变为 reject,整个async函数都会中断执行。 有时,咱们那但愿前面一个异步操做失败,也不要中断后面的异步操做。这时能够将第一个await 放在 try catch 结构里面,这样无论这个异步是否是成功,第二个都会执行。 或者 在await的后面Promise对象后添加一个 catch方法,处理前面可能出现的错误。
若是 await 后面异步操做出错,那么等于 async 函数返回的 Promise 对象被 reject。
防止出错的方法也是将其放在 try...catch 代码块中。
async function f(){
try {
await new Promise((resolve,reject) => {
throw new Error("出错了")
})
} catch(e){
}
return await('hello world')
}
复制代码
若是有多个await,能够统一放在 try catch 代码块中
async function f(){
try {
var val1 = await ...
var val2 = await ...
var val3 = await ...
console.log('final',val3)
} catch(e){
console.log(e)
}
}
复制代码
使用 try catch 实现屡次重复尝试
async function test(){
let i
for(i = 0;i<3;++i){
try{
await ...
break
} catch(err){}
}
console.log(i) // 3
}
复制代码
第一点:await命令后面的Promise对象的结果kennel rejected,因此,最好把await放在 try catch代码块中。
第二点:多个 await 命令后面的异步若是不存在 继发关系,最好让他们同时触发。
let foo = await getFoo()
let bar = await getBar()
复制代码
上面代码中,getFoo 和 getBar 是两个独立的异步(互不依赖)被写成继发关系。这样比较耗时,由于只有getFoo完成之后才会执行 getBar,彻底可让他们同时触发。
// 写法 1
let [foo,bar] = await Promise.all([getFoo(),getBar()])
// 写法 2
let fooPromise = getFoo()
let barPromise = getBar()
let foo = await fooPromise
let bar = await barPromise
复制代码
async 函数实现原理就是将Generator函数和自动执行器包装在一个函数里。
async function fn(){
}
// 等同于
function fn(args){
return spawn(function* (){
})
}
复制代码
async Promise Generator进行比较
假定某个DOM元素上面,部署了一系列动画,前一个结束,后一个才开始。若是有一个动画出错,就再也不执行,返回上一个成功执行的动画返回值。
首先是 Promise 写法
function chainAnimationsPromise(el,animations){
// ret 保存上一个动画返回值
var ret = null
// 新建一个Promise
var p = new Promise.resolve()
// 使用then添加全部动画
for(var anim of animations){
p = p.then(function(val){
ret = val
return anim(elem)
})
}
return p.catch(function(e){
return ret
})
}
复制代码
代码彻底是Promise的API,不容易看出语义
下面是Generator写法
function chainAnimationsGenerator(elem, animations) {
return spawn(function*(){
var ret = null
try {
for(var anim of animations){
ret = yield anim(elem)
}
} catch(e){
}
return ret
})
}
复制代码
这个写法的问题在于必须有一个任务运行器自动执行Generator函数,必须保证yield语句后面的表达式返回一个Promise。
最后是 async 写法
async function chainAnimationAsync(elem,animations){
var ret = null
try {
for(var anim of animations) {
ret = await anim(elem)
}
}catch(e){
}
return rett
}
复制代码
依次远程读取一组URL,按照读取顺序输出结果
promise
function loginOrder(urls){
// 远程读取全部url
const textPromises = urls.map(url => {
return fetch(url).then(res => res.text())
})
//按顺序输出
textPromises.reduce((chain,textPromise) => {
return chain.then(() => textPromise)
.then(text => console.log(txt))
},Promise.resolve())
}
复制代码
async
async function loginOrder(urls){
for(const url of urls){
const response = await fetch(url)
console.log(await response.text())
}
}
复制代码
以上写法是继发的,效率很低,咱们须要同时发出远程请求。
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内部是继发执行,外部是不受影响的。后面的for of 内部使用了await,所以实现了按顺序输出。