如何从yield *返回

huangapple go评论77阅读模式
英文:

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'

非常感谢 如何从yield *返回

英文:

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 如何从yield *返回

答案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 =&gt; setTimeout(() =&gt; resolve({value: &#39;next&#39;, done: false}), 500))
  },
  return () {
    return Promise.resolve({value: &#39;return&#39;, done: true})
  },
  [Symbol.asyncIterator] () {
    return this
  }
}

async function * yieldFromIterator () {
  yield &#39;start&#39;
  yield * asyncIterator
}

(async function() {
  const yieldingIterator = yieldFromIterator()
  console.log(await yieldingIterator.next())   // {value: &#39;start&#39;, done: false}
  console.log(await yieldingIterator.next())   // {value: &#39;next&#39;, done: false}
  console.log(await yieldingIterator.return()) // {value: &#39;return&#39;, done: true}
})().catch(error =&gt; {
  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 &#39;return&#39; 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) =&gt; new Promise(resolve =&gt; setTimeout(resolve, ms, ...args));

const asyncIterator = (async function*() {
  await delay(500);
  yield &#39;next&#39;;
  return &#39;return&#39;;
})();

async function * yieldFromIterator () {
  yield &#39;start&#39;
  return yield * asyncIterator // &lt;=== Added `return`
}

(async function() {
  const yieldingIterator = yieldFromIterator()
  console.log(await yieldingIterator.next()) // {value: &#39;start&#39;, done: false}
  console.log(await yieldingIterator.next()) // {value: &#39;next&#39;, done: false}
  console.log(await yieldingIterator.next()) // {value: &#39;return&#39;, done: true}
  // Changed to `next` −−−−−−−−−−−−−−^
})().catch(error =&gt; {
  console.error(error)
})

<!-- language: lang-css -->

.as-console-wrapper {
    max-height: 100% !important;
}

<!-- end snippet -->

huangapple
  • 本文由 发表于 2020年1月3日 17:59:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/59576490.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定