基本概念
- Generator 函数的语法糖
const { promisify } = require("util");
const path = require('path')
const file1 = path.join(__dirname, './text/1.txt')
const file2 = path.join(__dirname, './text/2.txt')
const readFileP = promisify(readFile)
function* f() {
let data1 = yield readFileP(file1)
console.log('耶,完成了1,数据是' + data1);
let data2 = yield readFileP(file2)
console.log('耶,完成了2,数据是' + data2);
}
//async函数的版本
async function f() {
let data1 = await readFileP(file1)
console.log('耶,完成了1,数据是' + data1);
let data2 = await readFileP(file2)
console.log('耶,完成了2,数据是' + data2);
}
async函数的版本就是将 Generator 函数的星号(*)替换成async,将yield替换成await
定义async函数
- 用async关键字定义async函数
async function f() {
let data1 = await readFileP(file1)
console.log('耶,完成了1,数据是' + data1);
let data2 = await readFileP(file2)
console.log('耶,完成了2,数据是' + data2);
}
执行async函数
-
相当于执行了自动运行的Generator函数
-
async函数如果返回的结果不是Promise,则会运行结果包装成一个Promise返回
async function f() {
console.log(1);
}
f().then(()=>{
console.log(2);
})
async function f() {
console.log(1);
return 'done'
}
f().then(value => {
console.log(value);
})
await关键字
async函数执行的过程中,每次遇到await关键字,会将控制权转回外部环境
-
await后面是Promise实例-
等到 Promise实例被resolve后,把本次
await到下次await之间的代码推到MircoTask(微任务)中等待执行 -
await的返回值是该Promise实例resolve的值
-
-
await后面不是Promise实例-
立即将本次
await到下次await之间的代码推到MircoTask(微任务)中等待执行 -
await的返回值是等于await后面表达式的值
-
// `await`后面是Promise 实例
async function f() {
let data = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve('a')
}, 2000)
})
console.log(data);
}
//f()
//console.log('end')
// `await`后面不是Promise 实例
async function f() {
let data = await 'a'
console.log(data);
}
f()
console.log('end');
//end
//a
async函数的错误处理
-
用
try..catch对await进行错误捕捉- Promise被reject或抛出错误,await之后的代码不会执行
async function f() {
try {
let data = await new Promise((resolve, reject) => {
setTimeout(() => {
reject('123')
}, 2000)
})
//后续代码无法执行
console.log('done');
}catch (e) {
console.log('发生错误:',e);
}
}
f()
async函数处理并发异步任务
- 并发执行,可以使用
Promise.all
/*并发处理异步*/
async function f() {
let time1 = new Date()
let [data1,data2] = await Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('123')
}, 2000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('123')
}, 3000)
})
])
console.log(data1,data2,'用时:'+ (new Date() - time1));
}
f()
async函数与Promise的对比
-
用
async更加简洁 -
在处理不同异步结果相互依赖
-
错误处理
-
if...else分支等情况时更加简便
const {readFile} = require('fs')
const { promisify } = require("util");
const path = require('path')
const file1 = path.join(__dirname, './text/1.txt')
const file2 = path.join(__dirname, './text/2.txt')
const file3 = path.join(__dirname, './text/3.txt')
const readFileP = promisify(readFile)
function f1() {
readFileP(file1).then(data1 =>{
console.log('耶,完成了1,数据是' + data1);
return readFileP(file2)
}).then(data2 => {
console.log('耶,完成了1,数据是' + data2);
return readFileP(file3)
}).then(data3 => {
console.log('耶,完成了1,数据是' + data3);
})
}
async function f2() {
let data1 = await readFileP(file1)
console.log('耶,完成了1,数据是' + data1);
let data2 = await readFileP(file2)
console.log('耶,完成了2,数据是' + data1 + data2);
let data3 = await readFileP(file3)
console.log('耶,完成了2,数据是' + data1 + data2 + data3);
}
f()
async/await的实现原理?
-
async最终返回一个Promise
-
写一个递归函数,让generator自动执行
- generator与async的差别是async里面的await 函数可以自动串行执行
-
generator的value和done状态是迭代器协议的返回值
- value是yield的返回值, done是false时,继续执行迭代器, done为true时,resolve结果
function asyncFn(genFn) {
const g = genFn()
return new Promise((resolve, reject) => {
function autoRunNext(g, nextVal) {
const { value, done } = g.next(nextVal)
// 迭代器未执行完
if (!done) {
value.then((res) => {
autoRunNext(g, res)
})
} else {
// 迭代器执行完
resolve(value)
}
}
// 第一次执行autoRunNext是用来启动遍历器,不用传参数
autoRunNext(g)
})
}
// 测试
const getData = (i) => new Promise((resolve) => setTimeout(() => resolve(`data${i}`), 500))
function* testG() {
const data1 = yield getData(1)
console.log('data1: ', data1)
const data2 = yield getData(2)
console.log('data2: ', data2)
return 'success'
}
asyncFn(testG).then((res) => {
console.log(res)
})