From 46f925d34453ff05495f4d91bbc380b1eb8b5461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=BC=E5=81=A5=E8=81=AA?= Date: Thu, 10 Sep 2026 11:38:15 +0800 Subject: [PATCH] fix: propagate async iterator errors from tryEach --- lib/tryEach.js | 6 ++++-- test/es2017/asyncGenerators.js | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/lib/tryEach.js b/lib/tryEach.js index 8fa314b1f..e332c808c 100644 --- a/lib/tryEach.js +++ b/lib/tryEach.js @@ -1,12 +1,14 @@ import eachSeries from './eachSeries.js' import wrapAsync from './internal/wrapAsync.js' import awaitify from './internal/awaitify.js' +import breakLoop from './internal/breakLoop.js' /** * It runs each task in series but stops whenever any of the functions were * successful. If one of the tasks were successful, the `callback` will be * passed the result of the successful task. If all tasks fail, the callback * will be passed the error and result (if any) of the final attempt. + * If iterating over an async collection fails, the callback receives that error. * * @name tryEach * @static @@ -53,9 +55,9 @@ function tryEach(tasks, callback) { result = args; } error = err; - taskCb(err ? null : {}); + taskCb(null, err ? undefined : breakLoop); }); - }, () => callback(error, result)); + }, err => callback(err || error, result)); } export default awaitify(tryEach) diff --git a/test/es2017/asyncGenerators.js b/test/es2017/asyncGenerators.js index c5a7ecc66..e4c00549f 100644 --- a/test/es2017/asyncGenerators.js +++ b/test/es2017/asyncGenerators.js @@ -28,6 +28,41 @@ module.exports = function () { this.retries(3); + it('should reject tryEach when the async generator fails before yielding a task', async () => { + const error = new Error('iterator failed') + async function * tasks () { + yield await Promise.reject(error) + } + + await async.tryEach(tasks()).then( + () => { throw new Error('expected tryEach to reject') }, + err => { expect(err).to.equal(error) } + ) + }) + + it('should report tryEach iterator errors after a failed task', async () => { + const error = new Error('iterator failed') + async function * tasks () { + yield cb => cb(new Error('task failed'), 'last result') + throw error + } + + const outcome = await new Promise(resolve => { + async.tryEach(tasks(), (err, result) => resolve({err, result})) + }) + expect(outcome.err).to.equal(error) + expect(outcome.result).to.equal('last result') + }) + + it('should stop tryEach reading an async generator after a successful task', async () => { + async function * tasks () { + yield cb => cb(null, 'result') + throw new Error('should not request another task') + } + + expect(await async.tryEach(tasks())).to.equal('result') + }) + it('should handle async generators in each', (done) => { const calls = [] async.each(range(5),