【发布时间】:2019-12-10 17:41:34
【问题描述】:
在 javascript 中,允许迭代器具有 throw(error) 和 return(value) 方法。 return(value) 让迭代器有机会看到value,并有望返回{value: value, done: true}。 throw(error) 使迭代器有机会查看并可能捕获错误。如果错误被捕获,throw 应该返回下一个值。如果异常没有被捕获,它应该等价于return(undefined)。以下是这些机制的实际应用示例:
function* test() {
try {
yield 1;
} catch(e) {}
const fromConsumer = yield 2;
yield fromConsumer;
// I know no way to access the value passed to return in a generator
}
const iter = test()[Symbol.iterator]();
console.log(iter.next());
console.log(iter.throw(new Error('catch me')));
console.log(iter.next(9));
console.log(iter.next());
console.log(iter.return(0));
(与jsfiddle 相同)
我的问题是:为什么?有没有人有针对迭代器的这种控制 API 反转的合理用例?在什么情况下,迭代器处理在使用它时发生的错误实际上是有意义的?鉴于肯定会结束迭代器并且不能进一步影响迭代器 API 的行为,您何时希望将值传递给 return?
我会说我知道的一个用例是 redux-saga,他们在其中利用了迭代器的控制反转 API,似乎是穷人的 async/await。如果有人熟悉该工具的设计或使用,他们的选择还有其他好处吗?
【问题讨论】:
-
我没有时间就这个问题发表一个正确的答案,并且怀疑有些人会说这个问题对于 SO 来说过于开放或基于意见(什么是“可辩护的”用例) ?),但是:查看协程。虽然允许迭代器拥有
return和throw,但真正有用的是生成器。使用生成器,您可以构建协程。协程不仅仅是“穷人的async/await”。 -
“通常,这些方法的调用者应该在调用它们之前检查它们的存在。某些 ECMAScript 语言功能,包括
for-of、yield*,以及数组解构在执行存在检查后调用这些方法. 大多数接受 Iterable 对象作为参数的 ECMAScript 库函数也有条件地调用它们。”(来源:tc39.es/ecma262/#table-54) -
关于 redux saga 问题板的讨论也颇具启发性。他们指出,与 async/await 不同,可以进行同步执行(正如另一个答案中提到的,提前返回)。
标签: javascript iterator redux-saga