【JavaScript】用 Generator 函数实现 async/await 语法
2023/01/31 17:19:29
async/await
是 Generator 函数的语法糖。
示例
const getData = () =>
new Promise((resolve) => setTimeout(() => resolve("data"), 1000));
async function test() {
const data = await getData();
console.log("data: ", data);
const data2 = await getData();
console.log("data2: ", data2);
return "success";
}
// 这样的一个函数 应该再1秒后打印data 再过一秒打印data2 最后打印success
test().then((res) => console.log(res));
用 Generator 实现
对于上面示例,如果用 Generator 来实现,可以改写为以下形式:
function* testGen() {
const data = yield getData();
console.log("data: ", data);
const data2 = yield getData();
console.log("data2: ", data2);
return "success";
}
Generator 函数需要调用 .next()
方法才能往下执行,所以需要封装一个自动执行函数:
const test = asyncToGenerator(testGen);
test().then((res) => console.log(res));
思考之后完成如下实现:
function asyncToGenerator(gen) {
return new Promise((resolve, reject) => {
const iterator = gen();
(function exec(value, done = false) {
const result = iterator.next(value);
if (result.done) {
return resolve(result.value);
}
return Promise.resolve(result.value).then(exec, reject);
})();
});
}
asyncToGenerator()
方法:- 接收一个 Generator 函数,返回一个执行器方法,这个方法会异步执行每一个 yield 语句,最终返回一个 Promise。
asyncToGenerator()
内部的exec()
方法:- 接收 Generator 函数生成的迭代器,不断调用
.next()
方法直至完成所有 yield 任务。 exec()
方法在done === true
后立即 resolve,不需要关注 Generator 函数最后的 return 值类型,因为最后的 return 值会由外部的 then 方法处理(test().then())。
- 接收 Generator 函数生成的迭代器,不断调用
完整例子
const getData = () => {
return new Promise((resolve) =>
setTimeout(() => resolve(Math.random()), 1000)
);
};
function asyncToGenerator(gen) {
return new Promise((resolve, reject) => {
const iterator = gen();
(function exec(value, done = false) {
const result = iterator.next(value);
if (result.done) {
return resolve(result.value);
}
if (result.value instanceof Promise) {
result.value.then(exec).catch(reject);
} else {
exec(result.value);
}
})();
});
}
function* testGen() {
const data = yield getData();
console.log("data: ", data);
const data2 = yield getData();
console.log("data2: ", data2);
return Promise.resolve(122);
}
const test = () => asyncToGenerator(testGen);
test().then(console.log).catch(console.log);