JavaScript中可通过生成器和async/await模拟协程控制,二者分链做培别基于不同机制实现协作式暂停与恢复,适用于不同场景且可结合使用。
生成器(Generators)实现协作式暂停与恢复function* taskRunner() { console.log('任务A:开始'); yield '等待外部指令1'; console.log('任务B:进行中'); yield '等待外部指令2'; console.log('任务C:完成'); return '所有任务完成'; }- 恢复:调用生成器对象的next()方法,函数从上次yield的地方继续执行,直到遇到下一个yield或函数结束。next()方法返回一个对象,包含value(yield表达式的值)和done(表示生成器是否已完成)两个属性。如上述例子中,每次调用runner.next()都会恢复生成器执行并返回相应结果。
- 双向通信:通过next()方法可向生成器内部传递参数,实现双向通信。例如:
function* generatorWithInput() { const input1 = yield '请输入第一个值'; console.log('接收到的第一个值:', input1); const input2 = yield '请输入第二个值'; console.log('接收到的第二个值:', input2); return '输入完成';}const gen = generatorWithInput();console.log(gen.next()); // { value: '请输入第一个值', done: false }console.log(gen.next(10)); // { value: '请输入第二个值', done: false },传递参数10给生成器console.log(gen.next(20)); // { value: '输入完成', done: true },传递参数20给生成器- 应用场景:
自定义迭代器或无限序列:可惰性地生成值,避免一次性计算所有结果,如斐波那契数列生成器。
构建复杂的异步流程控制框架:如Redux Saga利用生成器的yield暂停和控制副作用,以同步方式编写复杂异步逻辑,提供比async/await更细粒度的控制能力,可yield出任何东西。
模拟协作式多任务处理(非阻塞的同步计算):对于需执行大量计算的函数,为不阻塞UI,可用生成器每次计算一小步后yield,让事件循环处理其他任务,下次next()调用时继续计算。
构建状态机:每个yield点可看作状态转换点,外部通过next()驱动状态迁移。
async/await实现协程控制- 定义与原理:async/await是ES2017引入的基于Promise的语法糖。async函数棚唯内部使用await关键字,它会等待一个Promise的解决。若Promise未解决,async函数暂停执行,将控制权交还给事件循环;Promise解决后,async函数作为微任务重新排队,从await处继续执行。例如:
function simulateAsyncOperation(ms) { return new Promise(resolve => setTimeout(resolve, ms));}async function fetchData() { console.log('开始获取数据...'); await simulateAsyncOperation(1000); console.log('数据A获取完成。'); await simulateAsyncOperation(500); console.log('数据B获取完成。'); return '所有数据已就绪';}fetchData().then(result => { console.log(result);});console.log('主线程继续执行,不会被阻塞。');- 与生成器的区别:
底层机制:生成器是更底层、显式、可编程的暂停/恢复原语;async/await是针对异步编程场景的高级抽象,其“暂停”与Promise和事件循环深度绑定。
驱动方式:生成器需手动调用next()方法驱动;async/await的暂停和恢复是隐式的,由Promise解决机制自动触发后续执行。
- 应用场景:
处理绝大多数异步操作:如网络请求、文件读写、定时器等基于Promise或回调的异步操作。
代码可读性和维护性优先:让异步代码看起来像同步代码,简化错误处理(使用try...catch),便于推理异步流程。
与现有Promise生态系统集成:现代JavaScript的异步API几乎都返回Promise,async/await与它们无缝衔接。
生成器与async/await的选择及结合使用- 选择依据:根据具体需求和问题选择。处理常规异步操作、追求可读性和维护性、与Promise生态集成时选async/await;实现自定义迭代、协作式多任务、状态机或需细粒度控制的异步框架时选生成器。
- 结合使用:二者可结合,如一些高级异步库中,生成器可驱动async/await流程,实现更强大的控制流管理。