咳咳,各位观众老爷们,晚上好!今天咱们来聊聊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
函数可以暂停和恢复执行,所以错误可能发生在不同的阶段。我们需要考虑以下几种情况:
-
生成器函数内部的同步错误: 这类错误是最简单的,直接用
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
-
生成器函数内部的异步错误: 这种错误需要使用
await
和try...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
-
在
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!
-
使用
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
块,并继续执行。 -
使用
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
会被 reject
,catch
块会捕获错误,并继续 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...catch
、generator.throw()
、generator.return()
和 yield
表达式的返回值。只有这样,才能让它乖乖地为你服务,编写出更健壮、更灵活的异步代码。
好了,今天的讲座就到这里。希望大家有所收获!如果还有什么疑问,欢迎随时提问。 祝大家编程愉快, Bug 远离!