Mei*_*hes 2 javascript iterator generator typescript async-iterator
假设我有一个发电机:
function* source() {
yield "hello"; yield "world";
}
Run Code Online (Sandbox Code Playgroud)
我创建了可迭代对象,使用 for 循环进行迭代,然后在迭代器完全完成之前退出循环(返回完成)。
function run() {
for (let item of source()) {
console.log(item);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
问题:如何从可迭代端发现迭代器提前终止?
如果您尝试直接在生成器本身中执行此操作,似乎没有任何反馈:
function* source2() {
try {
let result = yield "hello";
console.log("foo");
} catch (err) {
console.log("bar");
}
}
Run Code Online (Sandbox Code Playgroud)
...既没有记录“foo”也没有记录“bar”。
我注意到打字稿将Iterator
(lib.es2015)定义为:
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
Run Code Online (Sandbox Code Playgroud)
我拦截了这些方法并记录了调用,并且看起来如果迭代器提前终止for-loop
——至少通过一个——然后return
调用该方法。如果消费者抛出错误,它也会被调用。如果循环被允许完全迭代的迭代器return
是不叫。
Return
黑客所以,我做了一些 hack 来允许捕获另一个可迭代的 - 所以我不必重新实现迭代器。
function terminated(iterable, cb) {
return {
[Symbol.iterator]() {
const it = iterable[Symbol.iterator]();
it.return = function (value) {
cb(value);
return { done: true, value: undefined };
}
return it;
}
}
}
function* source() {
yield "hello"; yield "world";
}
function source2(){
return terminated(source(), () => { console.log("foo") });
}
for (let item of source2()) {
console.log(item);
break;
}
Run Code Online (Sandbox Code Playgroud)
它有效!
你好
富
删除break
,你会得到:
你好
世界
yield
在输入此答案时,我意识到更好的问题/解决方案是在原始生成器方法中查找。
我可以看到将信息传递回原始迭代的唯一方法是使用next(value)
. 因此,如果我们选择一些唯一值(例如Symbol.for("terminated")
)来表示终止,并且我们将上述 return-hack 更改为调用it.next(Symbol.for("terminated"))
:
function* source() {
let terminated = yield "hello";
if (terminated == Symbol.for("terminated")) {
console.log("FooBar!");
return;
}
yield "world";
}
function terminator(iterable) {
return {
[Symbol.iterator]() {
const it = iterable[Symbol.iterator]();
const $return = it.return;
it.return = function (value) {
it.next(Symbol.for("terminated"));
return $return.call(it)
}
return it;
}
}
}
for (let item of terminator(source())) {
console.log(item);
break;
}
Run Code Online (Sandbox Code Playgroud)
成功!
你好
FooBar!
Return
如果你链接一些额外的变换迭代器,那么return
调用会通过它们级联:
function* chain(source) {
for (let item of source) { yield item; }
}
for (let item of chain(chain(terminator(source())))) {
console.log(item);
break
}
Run Code Online (Sandbox Code Playgroud)
你好
FooBar!
我已将上述解决方案包装为一个包。它同时支持[Symbol.iterator]
和[Symbol.asyncIterator]
。我对异步迭代器的情况特别感兴趣,尤其是当某些资源需要正确处理时。
有一种更简单的方法可以做到这一点:使用finally 块。
function *source() {
let i;
try {
for(i = 0; i < 5; i++)
yield i;
}
finally {
if(i !== 5)
console.log(' terminated early');
}
}
console.log('First:')
for(const val of source()) {
console.log(` ${val}`);
}
console.log('Second:')
for(const val of source()) {
console.log(` ${val}`);
if(val > 2)
break;
}
Run Code Online (Sandbox Code Playgroud)
...产量:
First:
0
1
2
3
4
Second:
0
1
2
3
terminated early
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
480 次 |
最近记录: |