JS `async Generator` 函数的错误处理与 `yield` 表达式的异步返回值

咳咳,各位观众老爷们,晚上好!今天咱们来聊聊JavaScript中async generator这个磨人的小妖精,以及如何驯服它,特别是关于错误处理和yield表达式异步返回值这两大难题。准备好了吗?Let’s go!

Async Generator:初识小妖精

首先,咱们得搞清楚async generator是个什么玩意儿。简单来说,它就是async函数和generator函数的结合体。它既可以像async函数一样处理异步操作,又可以像generator函数一样分段执行,并用yield关键字暂停和恢复执行。

它的基本语法是这样的:

async function* myAsyncGenerator() {
  yield 1;
  yield await Promise.resolve(2); // 异步操作
  yield 3;
}

async function main() {
  const generator = myAsyncGenerator();

  for await (const value of generator) {
    console.log(value); // 1, 2, 3
  }
}

main();

这里,async function* 定义了一个异步生成器函数。yield 关键字用于产生值。注意,当 yield 后面跟着一个 Promise 时,需要使用 await 关键字等待 Promise 完成。 循环迭代时,使用 for await...of 而不是 for...of

错误处理:抓住小妖精的尾巴

async generator的错误处理可比普通的async函数要复杂一些。因为generator函数可以暂停和恢复执行,所以错误可能发生在不同的阶段。我们需要考虑以下几种情况:

  1. 生成器函数内部的同步错误: 这类错误是最简单的,直接用try...catch捕获就行了。

    async function* myAsyncGenerator() {
      try {
        yield 1;
        throw new Error("Something went wrong!");
        yield 2; // 不会被执行
      } catch (error) {
        console.error("Caught an error:", error.message);
      }
      yield 3;
    }
    
    async function main() {
      const generator = myAsyncGenerator();
    
      for await (const value of generator) {
        console.log(value);
      }
    }
    
    main(); // 输出: 1, Caught an error: Something went wrong!, 3
  2. 生成器函数内部的异步错误: 这种错误需要使用awaittry...catch结合处理。

    async function* myAsyncGenerator() {
      try {
        yield 1;
        await new Promise((resolve, reject) => {
          setTimeout(() => {
            reject(new Error("Async error!"));
          }, 100);
        });
        yield 2; // 不会被执行
      } catch (error) {
        console.error("Caught an async error:", error.message);
      }
      yield 3;
    }
    
    async function main() {
      const generator = myAsyncGenerator();
    
      for await (const value of generator) {
        console.log(value);
      }
    }
    
    main(); // 输出: 1, Caught an async error: Async error!, 3
  3. for await...of循环中处理错误: 如果在循环中使用生成器,并且生成器抛出了错误,循环会立即停止。

    async function* myAsyncGenerator() {
      yield 1;
      throw new Error("Generator error!");
      yield 2; // 不会被执行
    }
    
    async function main() {
      try {
        for await (const value of myAsyncGenerator()) {
          console.log(value);
        }
      } catch (error) {
        console.error("Caught generator error:", error.message);
      }
    }
    
    main(); // 输出: 1, Caught generator error: Generator error!
  4. 使用generator.throw()方法注入错误: generator.throw() 方法允许你在生成器函数内部抛出一个错误。 这可以模拟外部环境导致的错误。

    async function* myAsyncGenerator() {
      try {
        yield 1;
        const nextValue = yield 2; // 等待外部提供值
        console.log("Received:", nextValue); // 可能不会执行
        yield 3;
      } catch (error) {
        console.error("Caught injected error:", error.message);
      }
      yield 4;
    }
    
    async function main() {
      const generator = myAsyncGenerator();
    
      console.log(await generator.next()); // { value: 1, done: false }
      console.log(await generator.next()); // { value: 2, done: false }
    
      console.log(await generator.throw(new Error("Injected error!"))); // { value: 4, done: false }
      console.log(await generator.next()); // { value: undefined, done: true }
    }
    
    main(); // 输出: { value: 1, done: false }, { value: 2, done: false }, Caught injected error: Injected error!, { value: 4, done: false }, { value: undefined, done: true }

    这里,generator.throw() 方法在 yield 2 的地方注入了一个错误。 生成器函数会跳到 catch 块,并继续执行。

  5. 使用generator.return()方法提前结束生成器: generator.return() 方法允许你提前结束生成器函数,并返回一个指定的值。

    async function* myAsyncGenerator() {
      yield 1;
      yield 2;
      yield 3;
    }
    
    async function main() {
      const generator = myAsyncGenerator();
    
      console.log(await generator.next()); // { value: 1, done: false }
      console.log(await generator.return("Done!")); // { value: 'Done!', done: true }
      console.log(await generator.next()); // { value: undefined, done: true }
    }
    
    main(); // 输出: { value: 1, done: false }, { value: 'Done!', done: true }, { value: undefined, done: true }

    注意,generator.return() 会立即结束生成器,后续的 yield 语句将不会被执行。

yield 表达式的异步返回值:和小妖精玩捉迷藏

yield 表达式不仅可以产生值,还可以接收值。 当生成器函数恢复执行时,yield 表达式会返回一个值,这个值可以通过 generator.next(value) 方法传递。 如果 yield 表达式后面跟着一个 Promise,那么返回值可以是 Promise 完成后的值。

async function* myAsyncGenerator() {
  const value1 = yield 1;
  console.log("Received from yield 1:", value1);

  const value2 = yield await Promise.resolve(2);
  console.log("Received from yield 2:", value2);

  return 3;
}

async function main() {
  const generator = myAsyncGenerator();

  console.log(await generator.next()); // { value: 1, done: false }
  console.log(await generator.next("Hello")); // { value: 2, done: false }, Received from yield 1: Hello
  console.log(await generator.next("World")); // { value: 3, done: true }, Received from yield 2: World
}

main(); // 输出: { value: 1, done: false }, { value: 2, done: false }, Received from yield 1: Hello, { value: 3, done: true }, Received from yield 2: World

这里,yield 1 表达式返回的值是 "Hello"yield await Promise.resolve(2) 表达式返回的值是 "World"

错误处理和异步返回值结合:终极驯服

现在,让我们把错误处理和异步返回值结合起来,看看如何更优雅地驯服 async generator

async function* myAsyncGenerator() {
  try {
    const value1 = yield 1;
    console.log("Received from yield 1:", value1);

    const value2 = yield await new Promise((resolve, reject) => {
      setTimeout(() => {
        // 模拟一个异步操作,根据传入的值决定 resolve 还是 reject
        if (value1 === "error") {
          reject(new Error("Async operation failed!"));
        } else {
          resolve(2);
        }
      }, 100);
    });
    console.log("Received from yield 2:", value2);

    yield 3;
  } catch (error) {
    console.error("Caught an error:", error.message);
    yield 4; // 即使出错,也可以继续 yield
  }
}

async function main() {
  const generator = myAsyncGenerator();

  console.log(await generator.next()); // { value: 1, done: false }

  // 模拟成功的情况
  console.log(await generator.next("success")); // { value: 2, done: false }, Received from yield 1: success
  console.log(await generator.next()); // { value: 3, done: false }, Received from yield 2: undefined
  console.log(await generator.next()); // { value: undefined, done: true }

  // 重新创建一个生成器,模拟失败的情况
  const generator2 = myAsyncGenerator();
  console.log(await generator2.next()); // { value: 1, done: false }
  console.log(await generator2.next("error")); // { value: 4, done: false }, Received from yield 1: error, Caught an error: Async operation failed!
  console.log(await generator2.next()); // { value: undefined, done: true }
}

main();

在这个例子中,我们通过 generator.next() 方法向生成器函数传递不同的值,模拟异步操作的成功和失败。 如果传入的值是 "error"Promise 会被 rejectcatch 块会捕获错误,并继续 yield 值。

总结:驯服小妖精的秘诀

技巧点 说明 代码示例
try...catch 用于捕获生成器函数内部的同步和异步错误。 javascript async function* myAsyncGenerator() { try { yield 1; await Promise.reject(new Error("Async error!")); } catch (error) { console.error("Caught:", error.message); } }
for await...of 用于迭代异步生成器,并在循环中处理错误。 javascript async function main() { try { for await (const value of myAsyncGenerator()) { console.log(value); } } catch (error) { console.error("Loop caught:", error.message); } }
generator.throw() 用于在生成器函数内部抛出一个错误,模拟外部环境导致的错误。 javascript async function* myAsyncGenerator() { try { yield 1; const nextValue = yield 2; } catch (error) { console.error("Injected caught:", error.message); } } async function main() { const generator = myAsyncGenerator(); await generator.next(); await generator.throw(new Error("Injected!")); }
generator.return() 用于提前结束生成器函数,并返回一个指定的值。 javascript async function* myAsyncGenerator() { yield 1; yield 2; } async function main() { const generator = myAsyncGenerator(); await generator.next(); await generator.return("Done!"); }
yield 表达式的返回值 yield 表达式可以接收值,通过 generator.next(value) 方法传递。 如果 yield 后面跟着 Promise,返回值是 Promise 完成后的值。 javascript async function* myAsyncGenerator() { const value = yield 1; console.log("Received:", value); } async function main() { const generator = myAsyncGenerator(); await generator.next(); await generator.next("Hello"); }

总之,想要驯服 async generator 这个小妖精,需要掌握它的脾气,了解它的弱点,并灵活运用 try...catchgenerator.throw()generator.return()yield 表达式的返回值。只有这样,才能让它乖乖地为你服务,编写出更健壮、更灵活的异步代码。

好了,今天的讲座就到这里。希望大家有所收获!如果还有什么疑问,欢迎随时提问。 祝大家编程愉快, Bug 远离!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注