近期在看redux-saga源码,了解take,put,fork,takeEvery,all等的实现,里面全是generator(saga里面不可以用async+await)...
事实上除了在saga里面要写大量generator之外,我们平时在业务代码中更多会用async + await来处理异步逻辑。不过,了解generator是实现async+await基础。
简言之,async + await = co + generator + 返回promise + try catch处理错误捕获。
需要实现效果,应该和以下async+await的结果一致:
async function myAsync() {
let a = await Promise.resolve(1);
let b = await Promise.resolve(2);
let c = await Promise.resolve(3);
return a + b + c;
}
myAsync().then(res => {
console.log('async的结果:', res); // async的结果: 6
})
第一版: co + generator + 返回promise:
function* myGenerator() {
let a = yield Promise.resolve(1);
let b = yield Promise.resolve(2);
let c = yield Promise.resolve(3);
return a + b + c;
}
//期望:
run(myGenerator).then(res => {
console.log('mygenerator函数的结果,', res); // mygenerator函数的结果, 6
})
//实现run方法
function run(generator) {
return new Promise((resolve, reject) => {
let it = generator();
const next = (newValue) => {
let {value, done} = it.next(newValue);
if (!done) {
Promise.resolve(value).then(next);
// 用Promise.resolve包裹value值是预防yield后面为primitive值的情况
} else {
resolve(value)
}
}
next();
})
}
// 以上实现基本功能,下面看看如何进行错误捕获呢?
第二版: 加上错误捕获的处理:
首先需要了解一个知识点:在生成器generator执行后返回的迭代器iterator上有一个throw方法,可以接收一个错误信息,并且把它抛出取,如果generator函数里有try catch,则iterator抛出来的错误,可以被generator函数中的catch(e)给捕获到。
function* myGenerator2() {
try {
yield 1;
yield Promise.reject('我错啦😂!');
// 猜猜这里的错误,哪里会捕获到?是下面的catch?还是最后promise的catch?
} catch (error) {
console.log('generator函数里面catch到的错误', error)
}
}
function run(generator) {
return new Promise((resolve, reject) => {
let it = generator();
const next = (newValue) => {
let res;
try {
res = it.next(newValue);
} catch(err) {
return reject(err)
}
console.log('res', res); // 介里会是什么呢?
// res { value: Promise { <rejected> '我错啦😂!' }, done: false }
if (!res.done) {
Promise.resolve(res.value).then(next, err => it.throw(err)); // Here 看过来!
} else {
resolve(res.value);
}
}
next();
})
}
run(myGenerator2).then(res => {
console.log('mygenerator函数的结果,', res);
}).catch(e => {
console.log('放心,这里catch到啦!', e); // 是我这里捕获到了么?
})
// 答案来喽,不想看请蒙住:
// generator函数里面catch到的错误 我错啦😂!
可以发现,yield 后面函数处理发生错误,但是只要内部用it.throw()把接收到的错误扔出去,那么在外层generator函数里,只要写了try catch就可以被catch捕获到。出错后,迭代器便不往后继续迭代了。
MDN的解释:使用 throw方法向该生成器抛出一个异常,该异常通常可以通过 try...catch 块进行捕获。
那如果内部不使用throw,而是用reject去处理错误呢?即:
if (!res.done) {
Promise.resolve(res.value).then(next,reject); // 这里变成reject
} else {
resolve(res.value);
}
// 那么就是最后的catch方法接到了错误:“放心,这里catch到啦! 👀”
情况大致就是这样啦,下面9k字常文总结得更全面~ /:moon
参考文献: