1.其他异步操作
- 传统的异步操作
- Generator的异步操作
1.1传统的异步操作
传统的异步操作
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
1.1.1回调函数(callback)
将一个任务分成多段,嵌套的形式去执行
fs.readFile('/etc/passwd' , 'utf-8', function(err , data){ if (err) throw err; console.log(data); });
回调地狱callback hell
//不同的任务
function toEat(val, fn) {
setTimeout(() => {
fn(val)
}, 2000);
}
function toDrink(val, fn) {
setTimeout(() => {
fn(val)
}, 1000);
}
function toKTV(val, fn) {
setTimeout(() => {
fn(val)
}, 500);
}
//按顺序执行任务
toDrink("吃火锅", data => {
console.log(data);
toDrink("喝啤酒", data => {
console.log(data);
toKTV("唱歌", data => {
console.log(data);
})
})
})
//"吃火锅"
//"喝啤酒"
//"唱歌"
1.1.2 Promise
//任务封装Promise
function toResolve(data, delay) {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(data)
}, delay);
})
return promise
}
//不同的任务
function toEat(val) {
return toResolve(val, 2000)
}
function toDrink(val) {
return toResolve(val, 1000)
}
function toKTV(val) {
return toResolve(val, 500)
}
//按顺序执行任务
toEat("吃火锅").then(val => {
console.log(val);
return toDrink("喝啤酒")
}).then(val => {
console.log(val);
return toKTV("唱歌")
}).then(val => {
console.log(val);
}).catch(res => {
console.log(res);
})
//吃火锅
//喝啤酒
//唱歌
1.2.Generator的异步操作
仍然使用上面封装的Promise任务,使用Generator去异步执行
//生成器函数
function* asyncJob() {
let val1 = yield toEat("吃火锅")
console.log(val1);
let val2 = yield toDrink("喝啤酒")
console.log(val2);
let val3 = yield toKTV("唱歌")
console.log(val3);
}
//生成器函数的执行器
function runGenerator(fn) {
let iter = fn()
let { value, done } = iter.next()
value.then(val1 => {
let { value, done } = iter.next(val1)
value.then(val2 => {
let { value, done } = iter.next(val2)
value.then(val3 => {
iter.next(val3)
})
})
})
}
//开始执行异步任务
runGenerator(asyncJob)
//吃火锅
//喝啤酒
//唱歌
以toEat为例,观察异步任务如何执行的
- 调用生成器函数的执行器
- 调用生成器函数,返回迭代器对象
- 第一次调用迭代器上的next方法:执行至第一个yild,调用toEat函数,返回一个Promise对象作为next返回值的value属性,解构取value的值
- 在value上调用then:接收resolve中的值(会延迟2s),通过第二次调用next将接收的值赋值给第一个yield
- 可以在第一个yield和第二个yield之间处理toEat返回的值
- 对于第二个next的返回值可以解构进行同样的操作
2.async函数
Generator的语法糖 - 使异步
操作更加简单
使用上面封装的Promise任务
//生成器的异步任务
async function asyncJob() {
let val1 = await toEat("吃火锅")
console.log(val1);
let val2 = await toDrink("喝啤酒")
console.log(val2);
let val3 = await toKTV("唱歌")
console.log(val3);
}
//开始执行异步任务
asyncJob()
//吃火锅
//喝啤酒
//唱歌
async函数对Generator的改进
- 内置执行器
Generator函数的执行需要执行器,手写或者引入co模块,而async函数自带执行器,可以让其像普通函数一样执行 - 更好的语义
async与await比星号*与yield的语义表达更清晰 - 更广的适用性
co模块指定yield后只能是Thunk函数
或Promise对象
,而async函数的await后可以是Promise对象
或原始类型的值
返回值是Promise
Genetator的返回值是迭代器对象
,async函数返回的是一个Promise对象
2.1 async函数的错误处理
已知async返回的是一个Promise,可以在外部捕获错误
//生成器的异步任务
async function asyncJob() {
let val1 = await toEat("吃火锅").then(() => {
throw new Error("饭店倒闭了")
})
console.log(val1);
let val2 = await toDrink("喝啤酒")
console.log(val2);
let val3 = await toKTV("唱歌")
console.log(val3);
}
//开始执行异步任务
asyncJob().catch(res => {
console.log(res);
})
// Error:饭店倒闭了
2.2 try...catch
//生成器的异步任务
async function asyncJob() {
try {
let val1 = await toEat("吃火锅").then(() => {
throw new Error("饭店倒闭了")
})
console.log(val1);
} catch (e) {
console.log(e);
}
try {
let val2 = await toDrink("喝啤酒")
console.log(val2);
let val3 = await toKTV("唱歌")
console.log(val3);
} catch (e) {
console.log(e);
}
}
//开始执行异步任务
asyncJob().catch(res => {
console.log(res);
})
// Error:饭店倒闭了
// 喝啤酒
// 唱歌
2.3 async函数中的并发
//生成器的异步任务
async function asyncJob() {
let val = await Promise.all([toEat("吃火锅"), toDrink("喝啤酒")])
console.log(val);
let val3 = await toKTV("唱歌")
console.log(val3);
}
//开始执行异步任务
asyncJob().catch(res => {
console.log(res);
})
// ['吃火锅','喝啤酒']
// "唱歌"
2.4 使用async函数重写一个异步的forEach
Array.prototype.asyncForEach = async function (callback, thisArg) {
for (let i = 0; i < this.length; i++) {
let maybePromise = callback.call(thisArg ?? window, this[i], i, this)
if (maybePromise instanceof Promise) {
await maybePromise
}
}
}
function sleep(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, time)
})
}
[1, 2, 3].asyncForEach(async val => {
await sleep(1000)
console.log(val);
})
//每间隔1000ms打印一次
//1
//2
//3