代码实现
function asyncTask1() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('result1');
}, 1000);
});
}
function asyncTask2(result1) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result1 + ' -> result2');
}, 1000);
});
}
function asyncTask3(result2) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result2 + ' -> result3');
}, 1000);
});
}
function* taskGenerator() {
const result1 = yield asyncTask1();
const result2 = yield asyncTask2(result1);
const result3 = yield asyncTask3(result2);
return result3;
}
function runGenerator(generator) {
const iterator = generator();
function step(result) {
let { value, done } = iterator.next(result);
if (done) {
return value;
}
return Promise.resolve(value).then(step);
}
return step();
}
runGenerator(taskGenerator).then((finalResult) => {
console.log(finalResult);
});
优势阐述
- 代码可读性:
- 在传统链式调用Promise中,随着异步任务增多,代码会形成“回调地狱”式的链式结构,使得代码难以阅读和维护。例如:
asyncTask1()
.then(result1 => asyncTask2(result1))
.then(result2 => asyncTask3(result2))
.then(finalResult => console.log(finalResult));
- 而使用生成器与迭代器,异步任务以更接近同步代码的方式书写,每个异步操作在生成器函数内清晰地按顺序排列,如上述
taskGenerator
函数,更易于理解和修改。
- 错误处理:
- 传统链式调用Promise时,若其中一个Promise被拒绝,错误处理逻辑在链式的
.catch
中,定位具体出错的任务可能较复杂,尤其是多层嵌套时。
- 生成器函数可以使用
try...catch
块来捕获错误,使得错误处理更直观和精准,更符合同步代码的错误处理习惯。例如:
function* taskGenerator() {
try {
const result1 = yield asyncTask1();
const result2 = yield asyncTask2(result1);
const result3 = yield asyncTask3(result2);
return result3;
} catch (error) {
console.error('Error occurred:', error);
}
}
- 代码复用:
- 生成器函数的每个部分可以单独提取和复用,生成器函数本身也可以作为一个独立的单元进行复用。例如,
asyncTask1
、asyncTask2
、asyncTask3
函数可以在其他生成器函数或异步操作中复用,而传统链式调用相对更耦合,复用性较差。