MST

星途 面试题库

面试题:JavaScript生成器状态管理与异步操作

在JavaScript中,生成器常与异步操作结合实现更优雅的异步流程控制。假设你需要处理一系列异步任务(例如依次发起多个网络请求),每个任务依赖前一个任务的结果,如何利用生成器及其状态管理来完成这个需求?请给出代码示例并解释原理。
39.2万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

代码示例

function* asyncTasks() {
    try {
        // 模拟第一个异步任务(这里用setTimeout模拟网络请求)
        const result1 = yield new Promise((resolve) => {
            setTimeout(() => {
                resolve('第一个任务结果');
            }, 1000);
        });
        console.log('第一个任务结果:', result1);

        // 模拟第二个异步任务,依赖第一个任务的结果
        const result2 = yield new Promise((resolve) => {
            setTimeout(() => {
                resolve('第二个任务结果,依赖 ' + result1);
            }, 1000);
        });
        console.log('第二个任务结果:', result2);

        // 模拟第三个异步任务,依赖第二个任务的结果
        const result3 = yield new Promise((resolve) => {
            setTimeout(() => {
                resolve('第三个任务结果,依赖 ' + result2);
            }, 1000);
        });
        console.log('第三个任务结果:', result3);
    } catch (error) {
        console.error('任务执行出错:', error);
    }
}

function runGenerator(generator) {
    const gen = generator();
    function step(result) {
        let { value, done } = result;
        if (done) return;
        if (value && typeof value.then === 'function') {
            value.then((data) => {
                step(gen.next(data));
            }).catch((error) => {
                gen.throw(error);
            });
        } else {
            step(gen.next(value));
        }
    }
    step(gen.next());
}

runGenerator(asyncTasks);

原理解释

  1. 生成器函数定义asyncTasks 是一个生成器函数,使用 function* 关键字定义。在生成器函数内部,通过 yield 关键字暂停函数执行,并返回一个值(这里返回的是 Promise 对象)。
  2. 异步任务模拟:使用 setTimeout 模拟网络请求,实际应用中可以替换为真实的网络请求库(如 fetch)。每次 yield 返回一个 Promise,当这个 Promise 被解决(resolved)时,yield 表达式的值就是 Promise 的解决值。
  3. 生成器状态管理与执行runGenerator 函数用于执行生成器。它首先通过 generator() 创建一个生成器实例 genstep 函数负责驱动生成器的执行,每次调用 gen.next() 来恢复生成器执行,并将 Promise 的解决值作为参数传递给 next。如果 yield 返回的是一个 Promise,则等待 Promise 解决后再继续执行下一步;如果 yield 返回的不是 Promise,则直接进入下一步。
  4. 错误处理:在生成器函数内部使用 try - catch 块捕获可能出现的错误,当 Promise 被拒绝(rejected)时,通过 gen.throw(error) 将错误传递到生成器内部的 catch 块进行处理。这样就实现了通过生成器及其状态管理来优雅地控制一系列有依赖关系的异步任务。