mfu*_*n26 14 javascript generator async-await
\n\n\n异步生成器的方法
\nreturn()就像return在当前挂起位置将一条语句插入到生成器主体中一样,该语句完成生成器并允许生成器在与块组合时执行任何清理任务try...finally。
那么为什么以下代码打印0\xe2\x80\x933而不是仅打印0\xe2\x80\x93 2?
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst values = (async function* delayedIntegers() {\n let n = 0;\n while (true) {\n yield n++;\n await delay(100);\n }\n})();\n\nawait Promise.all([\n (async () => {\n for await (const value of values) console.log(value);\n })(),\n (async () => {\n await delay(250);\n values.return();\n })(),\n]);\nRun Code Online (Sandbox Code Playgroud)\n我尝试添加日志语句以更好地理解“当前挂起位置”在哪里,并且从我调用该方法时可以看出return()实例AsyncGenerator没有挂起(主体执行不在语句中yield)而不是返回一旦到达yield语句,下一个值就会yielded被挂起,此时“返回”最终发生。
有什么方法可以检测该return()方法是否已被调用而不是yield之后调用?
我可以自己实现该AsyncIterator接口,但随后我失去了yield异步生成器支持的语法:
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\nconst values = (() => {\n let n = 0;\n let done = false;\n return {\n [Symbol.asyncIterator]() {\n return this;\n },\n async next() {\n if (done) return { done, value: undefined };\n if (n !== 0) {\n await delay(100);\n if (done) return { done, value: undefined };\n }\n return { done, value: n++ };\n },\n async return() {\n done = true;\n return { done, value: undefined };\n },\n };\n})();\n\nawait Promise.all([\n (async () => {\n for await (const value of values) console.log(value);\n })(),\n (async () => {\n await delay(250);\n values.return();\n })(),\n]);\nRun Code Online (Sandbox Code Playgroud)\n
\n\n为什么代码会打印
\n0\xe2\x80\x933而不是仅打印0\xe2\x80\x932?据我所知,当我调用该return()方法时,AsyncGenerator实例不会被挂起(主体执行不在语句中yield),并且不会在到达yield语句时返回,而是会编辑下一个值yield,然后在此时挂起“回归”终于发生了。
是的,正是如此。生成器已经在运行,因为for await \xe2\x80\xa6 of循环确实调用了它的.next()方法,因此生成器将在考虑.return()调用之前完成该操作。
您在异步生成器上调用的所有方法都会排队。(在同步生成器中,您会收到“ TypeError:生成器已在运行”)。可以通过立即调用next多次来证明这一点:
const values = (async function*() {\n let i=0; while (true) {\n await new Promise(r => { setTimeout(r, 1000); });\n yield i++;\n }\n})();\nvalues.next().then(console.log, console.error);\nvalues.next().then(console.log, console.error);\nvalues.next().then(console.log, console.error);\nvalues.return(\'done\').then(console.log, console.error);\nvalues.next().then(console.log, console.error);Run Code Online (Sandbox Code Playgroud)\r\n\n\n有什么方法可以检测该
\nreturn()方法是否已被调用而不是yield之后调用?
不,不是来自发电机内部。实际上,如果您已经付出了努力来产生它,那么您可能仍然应该产生该价值。
\n听起来您想要做的是当您希望发电机停止时忽略产生的值。您应该在for await \xe2\x80\xa6 of循环中执行此操作 - 您还可以使用它通过以下break语句来停止生成器:
const delay = (ms) => new Promise((resolve) => {\n setTimeout(resolve, ms);\n});\n\nasync function* delayedIntegers() {\n let n = 0;\n while (true) {\n yield n++;\n await delay(1000);\n }\n}\n\n(async function main() {\n const start = Date.now();\n const values = delayedIntegers();\n for await (const value of values) {\n if (Date.now() - start > 2500) {\n console.log(\'done:\', value);\n break;\n }\n console.log(value);\n }\n})();Run Code Online (Sandbox Code Playgroud)\r\n但如果您确实想从外部中止生成器,则需要一个带外通道来发出取消信号。您可以AbortSignal为此使用:
const delay = (ms, signal) => new Promise((resolve, reject) => {\n function done() {\n resolve();\n signal?.removeEventListener("abort", stop);\n }\n function stop() {\n reject(this.reason);\n clearTimeout(handle);\n }\n signal?.throwIfAborted();\n const handle = setTimeout(done, ms);\n signal?.addEventListener("abort", stop, {once: true});\n});\n\nasync function* delayedIntegers(signal) {\n let n = 0;\n while (true) {\n yield n++;\n await delay(1000, signal);\n }\n}\n\n(async function main() {\n try {\n const values = delayedIntegers(AbortSignal.timeout(2500));\n for await (const value of values) {\n console.log(value);\n }\n } catch(e) {\n if (e.name != "TimeoutError") throw e;\n console.log("done");\n }\n})();Run Code Online (Sandbox Code Playgroud)\r\n这实际上允许在超时期间停止生成器,而不是在整秒过去之后。
\n