英文:
How to return from yield *
问题
有没有办法从 yield * asyncIterator
中返回?
背景:
给定一个类似于这样的异步迭代器:
const asyncIterator = {
next () {
return new Promise(resolve => setTimeout(() => resolve('next'), 500))
},
return () {
return Promise.resolve('return')
},
[Symbol.asyncIterator] () {
return this
}
}
以下将永远不会调用它的 return
方法:
async * yieldFromIterator () {
yield 'start'
yield * asyncIterator
}
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // 'start'
console.log(await yieldingIterator.next()) // 'next'
console.log(await yieldingIterator.return()) // 永远不会解决
这个也不会:
async * yieldFromIterator () {
yield 'start'
for await (const result of asyncIterator) {
yield result
}
}
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // 'start'
console.log(await yieldingIterator.next()) // 'next'
console.log(await yieldingIterator.return()) // 永远不会解决
而下面当然会:
async * returnIterator () {
yield 'start'
return asyncIterator
}
const returningIterator = returnIterator()
console.log(await returningIterator.next()) // 'start'
console.log(await returningIterator.next()) // 'next'
console.log(await returningIterator.return()) // 'return'
非常感谢
英文:
Is there any way to return from a yield * asyncIterator
?
Background:
Given an async iterator like this:
const asyncIterator = {
next () {
return new Promise(resolve => setTimeout(() => resolve('next'), 500))
},
return () {
return Promise.resolve('return')
},
[Symbol.asyncIterator] () {
return this
}
}
the following will never invoke its return
method:
async * yieldFromIterator () {
yield 'start'
yield * asyncIterator
}
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // 'start'
console.log(await yieldingIterator.next()) // 'next'
console.log(await yieldingIterator.return()) // never resolved
neither does this:
async * yieldFromIterator () {
yield 'start'
for await (const result of asyncIterator) {
yield result
}
}
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // 'start'
console.log(await yieldingIterator.next()) // 'next'
console.log(await yieldingIterator.return()) // never resolved
while the following of course does:
async * returnIterator () {
yield 'start'
return asyncIterator
}
const returningIterator = returnIterator()
console.log(await returningIterator.next()) // 'start'
console.log(await returningIterator.next()) // 'next'
console.log(await returningIterator.return()) // 'return'
Thank you very much
答案1
得分: 3
你的asyncIterator
存在一个错误:Promise的完成值必须是一个结果对象(实现了IteratorResult接口的对象,具有{done, value}
的结构)。但你却用一个字符串来完成这个Promise。
如果你将其更新为返回一个结果对象,你会看到如下返回值:
const asyncIterator = {
next () {
return new Promise(resolve => setTimeout(() => resolve({value: 'next', done: false}), 500))
},
return () {
return Promise.resolve({value: 'return', done: true})
},
[Symbol.asyncIterator] () {
return this
}
}
async function * yieldFromIterator () {
yield 'start'
yield * asyncIterator
}
(async function() {
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // {value: 'start', done: false}
console.log(await yieldingIterator.next()) // {value: 'next', done: false}
console.log(await yieldingIterator.return()) // {value: 'return', done: true}
})().catch(error => {
console.error(error)
})
另外,asyncIterator
还有另一个问题(不一定是一个错误):它没有继承自%AsyncIteratorPrototype%和其下的链。理论上,代码可以向%AsyncIteratorPrototype%,%IteratorPrototype%等添加方法,但这些方法在asyncIterator
中是缺失的。
一般来说,异步生成器函数是创建异步迭代器对象的最佳方式(因为异步生成器就是异步迭代器),就像非异步生成器函数是创建迭代器的最佳方式一样。你可以享受到yield
语法和继承标准原型的好处。
话虽如此,如果你用异步生成器函数来实现asyncIterator
,你不会在return
调用中看到你的'return'
字符串。你需要调用next
,并在yieldFromIterator
中使用return
:
const delay = (ms, ...args) => new Promise(resolve => setTimeout(resolve, ms, ...args));
const asyncIterator = (async function*() {
await delay(500);
yield 'next';
return 'return';
})();
async function * yieldFromIterator () {
yield 'start'
return yield * asyncIterator // <=== 添加了`return`
}
(async function() {
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // {value: 'start', done: false}
console.log(await yieldingIterator.next()) // {value: 'next', done: false}
console.log(await yieldingIterator.next()) // {value: 'return', done: true}
// 改成了`next` −−−−−−−−−−−−−−^
})().catch(error => {
console.error(error)
})
英文:
Your asyncIterator
has an error: The fulfillment value of the promise must be a result object (an object implementing the IteratorResult interface, which has the shape {done, value}
). But you're fulfilling the promise with a string instead.
If you update it to return a result object instead, you'll see the return:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const asyncIterator = {
next () {
return new Promise(resolve => setTimeout(() => resolve({value: 'next', done: false}), 500))
},
return () {
return Promise.resolve({value: 'return', done: true})
},
[Symbol.asyncIterator] () {
return this
}
}
async function * yieldFromIterator () {
yield 'start'
yield * asyncIterator
}
(async function() {
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // {value: 'start', done: false}
console.log(await yieldingIterator.next()) // {value: 'next', done: false}
console.log(await yieldingIterator.return()) // {value: 'return', done: true}
})().catch(error => {
console.error(error)
})
<!-- language: lang-css -->
.as-console-wrapper {
max-height: 100% !important;
}
<!-- end snippet -->
Separately, asyncIterator
has another issue (not necessarily a bug): It doesn't inherit from %AsyncIteratorPrototype% and the chain below it. In theory, code could add methods to %AsyncIteratorPrototype%, %IteratorPrototype%, etc., and those would be missing from asyncIterator
.
In general, async generator functions are the best way to create async iterator objects (because async generators are async iterators), just like non-async generator functions are the best way to create iterators. You get the benefit of yield
syntax and inheriting from the standard prototypes.
That said, if you implemented asyncIterator
with an async generator function, you wouldn't see your 'return'
string in response to that return
call. You'd need to call next
, and use return
in yieldFromIterator
:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const delay = (ms, ...args) => new Promise(resolve => setTimeout(resolve, ms, ...args));
const asyncIterator = (async function*() {
await delay(500);
yield 'next';
return 'return';
})();
async function * yieldFromIterator () {
yield 'start'
return yield * asyncIterator // <=== Added `return`
}
(async function() {
const yieldingIterator = yieldFromIterator()
console.log(await yieldingIterator.next()) // {value: 'start', done: false}
console.log(await yieldingIterator.next()) // {value: 'next', done: false}
console.log(await yieldingIterator.next()) // {value: 'return', done: true}
// Changed to `next` −−−−−−−−−−−−−−^
})().catch(error => {
console.error(error)
})
<!-- language: lang-css -->
.as-console-wrapper {
max-height: 100% !important;
}
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论