1、初识 async 函数
ES6中提供了两个很好的解决异步操作的方案 Promise 和 Generator,ES2017标准中引入的 async 函数就是建立在 Promise 和 Generator的基础之上,它是 Generator函数的语法糖,使异步操作更加方便
先通过一个异步读取文件的小栗子来对比下Promise、Generator 和 async 的异同点
const fs = require('fs')
function readFile(fileName) {
return new Promise((resolve, reject) => {
fs.readFile(fileName, (err, data) => {
if(err) {
reject(err)
}
resolve(data.toString())
})
})
}
(1)、通过 Promise 读取文件
readFile('data/a.txt').then(res => console.log(res))
.catch(err => console.log(err))
readFile('data/b.txt').then(res => console.log(res))
.catch(err => console.log(err))
readFile('data/c.txt').then(res => console.log(res))
.catch(err => console.log(err))
(2)、通过 Generator 函数读取文件
与 Promise 相比较,优点:把所有的接口都封装在一个函数里面了,缺点:代码量稍微多一点点
function* gen() {
yield readFile('data/a.txt')
yield readFile('data/b.txt')
yield readFile('data/c.txt')
}
let it = gen()
it.next().value.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
it.next().value.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
it.next().value.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
(3)、通过 async 函数读取文件
充分吸取了 Promise 和 Generator的优点,同时避免了它们的缺点
async function read() {
let readA = await readFile('data/a.txt')
let readB = await readFile('data/b.txt')
let readC = await readFile('data/c.txt')
console.log(readA)
console.log(readB)
console.log(readC)
}
read()
最终的输出结果
通过上例可以看出,async 函数就是将 Generator函数的星号("*")替换成了 async,把 yield 替换成了 await
async 函数对 Generator函数的改进,主要体现在三个方面:
(1)、内置执行器
async函数的执行,与普通函数一模一样,只需一行;而 Generator函数,需要调用next 方法
(2)、返回值是 Promise
async函数的返回值是Promise,这比 Generator函数返回一个 Iterator对象方便多了
(3)、更好的语义化
从字面意思上来讲,async是英文单词 asynchronous 的缩写,表示异步的;await中的 wait 是等待的意思。
因此相比 Generator函数中的星号和yield,语义更加清楚,async 表示这是一个异步操作的函数,await 表示紧跟在后面的表达式需要等待结果
2、async 函数的多种使用形式
// 函数声明
async function foo() {
// ....
}
// 函数表达式
let foo = async function() {
// ....
}
// 箭头函数
let foo = async() => {}
// 对象的方法
let obj = {
name: 'Roger',
async foo() {
}
}
obj.foo().then(res => {
// ....
})
// 类的方法
class Student{
constructor(name, age) {
this.name = name
this.age = age
}
async say() {
return `My name is ${this.name}, I'm ${this.age} years old !`
}
}
let jim = new Student('Jim Green', 13)
jim.say().then(res => console.log(res)) // My name is Jim Green, I'm 13 years old !
3、基本用法
async 函数返回一个 Promise 实例对象,可以使用 then 方法添加回调函数。
当函数执行时,一旦遇到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
// 休眠 ms 毫秒
function sleep(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
async function print(ms) {
console.log('start... ...')
await sleep(ms)
console.log('end... ...')
}
print(1000)
(1)、async 函数内部 return语句返回的值,会成为then方法回调函数的参数
async function foo() {
return 'hello world'
}
foo().then(res => console.log(res)) // hello world
(2)、async 函数内部抛出错误,会导致返回的 Promise对象变成reject状态,抛出的错误会被catch方法回调函数接收到
async function bar() {
return new Error('Error... ...')
}
bar().then(res => console.log(res))
.catch(err => console.log(err)) // Error: Error... ...
(3)、只有 async 函数内部的异步操作执行完,才会执行 then方法指定的回调函数
async function baz() {
await new Promise(resolve => {
console.log('执行第一个异步操作')
setTimeout(resolve, 2000)
})
await new Promise(resolve => {
console.log('执行第二个异步操作')
setTimeout(resolve, 3000)
})
return '异步执行完毕再执行then方法'
}
baz().then(res => {console.log(res)})
4、await 命令
await 用于等待一个 Promise对象,它只能在一个 async函数中使用
[return_value] = await expression
表达式:一个 Promise对象或者任何要等待的值
返回值:返回 Promise对象的处理结果。如果等待的不是 Promise对象,则返回该值本身
await命令会暂停当前 async函数的执行,等待 Promise处理完成。如果 Promise正常处理,其回调的 resolve函数参数会作为 await表达式的返回值,继续执行 async函数。如果 Promise处理异常,await表达式会把 Promise的异常原因抛出
// 如果 await 命令后的表达式的值不是一个 Promise,则返回该值本身
async function foo() {
return await 123
}
foo().then(res => console.log(res)) // 123
// 如果 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的返回值
async function bar() {
let f = await new Promise((resolve, reject) => {
resolve('我是表达式的返回值')
})
console.log(f) // 我是表达式的返回值
return 'ending'
}
bar().then(res => {console.log(res)}) // ending
// 如果 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出
async function baz() {
await new Promise((resolve, reject) => {
reject(new Error('出错啦......'))
})
}
baz().then(res => console.log(res))
.catch(err => console.log(err)) // Error: 出错啦......
(1)、任何一个 await语句后面的 Promise对象变为 reject状态,那么整个 async函数都会中断执行
async function foo() {
await Promise.reject('error')
return 'ending' // 未执行
}
foo().then(res => console.log(res))
// Uncaught (in promise) error
(2)、如果希望当前面的异步操作失败时,不要中断后面的异步操作,可以把前面的 await放在try...catch结构里面
async function foo() {
try{
await Promise.reject('error')
} catch(e) {
}
return await Promise.resolve('执行完毕')
}
foo().then(res => console.log(res)) // 执行完毕
还可以在 await后面的 Promise对象再跟一个 catch方法,处理前面可能出现的错误
async function foo() {
await Promise.reject('error').catch(err => console.log(err))
return '执行完毕'
}
foo().then(res => console.log(res)) // 执行完毕
(3)、如果想让多个异步操作同时触发,缩短程序的执行时间,可以参考如下两种写法
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
原文出处:https://www.cnblogs.com/rogerwu/p/10784236.html
来源:oschina
链接:https://my.oschina.net/u/4271175/blog/3265037