JavaScript 异步编程:async、await 的实现原理
#javascript
目录
- 1. 预备知识
- 2. 生成器 vs async/await
- 3. Thunk vs Promise
- 4. co 函数 vs async/await
- 5. 协程 vs 其他概念
- 6. co 函数的最简实现
1. 预备知识
1.1. 回调函数
略
1.2. Promise
略
1.3. generate 协程
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = numberGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
1.4. Thunk 函数与 thunkify
- Thunk 是一种将多参数函数转换为单参数的偏应用函数的方法
- Thunk通常用于将
回调风格
的函数转换为promise 风格
// 普通的回调风格函数
function fetchData(callback) {
// 模拟异步操作
setTimeout(() => callback(null, 'data'), 1000);
}
// Thunk 函数
function thunkify(fn) {
return function(...args) {
return function(callback) {
return fn.call(this, ...args, callback);
}
}
}
const thunkedFetchData = thunkify(fetchData);
const thunk = thunkedFetchData();
thunk((err, data) => console.log(data));
1.5. co 函数
- co 是一个著名的
npm 包
,它是一个生成器执行器,允许你使用同步的方式编写异步代码。 - 它可以自动执行生成器函数,并处理
yield
的 Promise、thunks 或 数组/对象。
const co = require('co');
co(function* () {
const result = yield Promise.resolve(1);
return result;
}).then(value => console.log(value));
2. 生成器 vs async/await
- 生成器是更底层的机制,需要手动迭代或使用执行器。
- async/await 是建立在生成器和 Promise 之上的高级语法,提供了更简洁的异步编程方式。
- async/await 可以看作是生成器和自动执行器的语法糖。
3. Thunk vs Promise
- Thunk 主要用于将回调风格的函数转换为更易于操作的形式。
- Promise 提供了一种标准的异步操作表示方法,具有更丰富的功能(如 .then()、.catch() 等)。
4. co 函数 vs async/await
co 函数
是 async/await 出现之前的一种解决方案,用于简化生成器的使用。async/await
可以看作是co 函数
的语言级实现
,提供了更原生和简洁的语法。
5. 协程 vs 其他概念
协程
是一个更广泛的概念,生成器是 JavaScript 中协程
的一种实现。async/await、Thunk、co 函数
都可以看作是基于协程思想的不同层次的抽象和实现。
6. co 函数的最简实现
co 函数的核心功能是自动执行生成器函数,将 yield
的值转换为 Promise,并处理异步流程。
以下是一个最简单的 co 函数
实现:
function co(gen) {
// 如果传入的是生成器函数,先执行它得到生成器对象
const generator = typeof gen === 'function' ? gen() : gen;
// 返回一个 Promise
return new Promise((resolve, reject) => {
// 定义递归处理函数
function step(nextValue) {
let result;
try {
// 执行下一步,传入上一步的值
result = generator.next(nextValue);
} catch (e) {
// 如果执行出错,reject Promise
return reject(e);
}
// 如果生成器执行完毕,resolve 最终值
if (result.done) {
return resolve(result.value);
}
// 将 yield 的值转换为 Promise
Promise.resolve(result.value)
.then(value => step(value)) // 继续执行下一步
.catch(reject); // 处理错误
}
// 开始执行
step();
});
}
6.1. 使用建议
- 在现代JavaScript中,推荐使用 async/await 而不是 co
- async/await 可以看作是这种模式的语言级实现
- 理解 co 的实现有助于深入理解 async/await 的工作原理
- 在需要支持旧版本环境时,co 仍然是一个有用的工具