【问题标题】:Why doesn't async eachOf returns value in callback but resolve为什么异步eachOf不在回调中返回值但解析
【发布时间】:2020-04-27 22:06:42
【问题描述】:

我使用来自asnyc frameworkeachOf 遍历js 数组,并在每次迭代中都有一个异步函数。如果调用callback 并且没有引发错误,则代码到达return 语句。我想知道为什么返回被忽略并且不会立即离开承诺菊花链。如果我在eachOf 函数周围包装另一个promise 并解析回调函数中的值,则第一级promise 将按预期完成。

这是为什么呢?回调函数中的返回不应该足以结束函数吗?

app1.js

const module1 = require('./module1');
const errorHandler = require('./error'); //implements custom error handler, e.g. log, mail, prowl, etc.

module1.getSomeVals()
.then(result => {
    console.log('this is result:', result); //this returns undefined because the return in module1 not working
})
.catch(e => {
    errorHandler.handle(e);
});

module1.js

const async = require('async'); //node module
const db = require('./db'); //custom db class

const module1 = {};

module1.getSomeVals= () => {
    return new Promise(resolve => {

        //some asyncronous function resolves values for next chain element, e.g. database stream
        const resultFromSomeAsyncFunction = [
            {
                foo: 'foo1',
                bar: 'bar1'
            },
            {
                foo: 'foo2',
                bar: 'bar2'
            },
            {
                foo: 'foo3',
                bar: 'bar3'
            },
        ];

        resolve(resultFromSomeAsyncFunction);
    })
    .then(results => {

        let newResults = [];

        async.eachOf(results, (result, index, callback) => {

            return db.query("select * from names where foo = '" + result.foo + "' and bar = '" + result.foo)
            .then(rows => {

                newResults.push(rows);
                console.log('this is rows:', rows);
                callback(null); //doesn't need second parameter, because newResults defined above
            })
            .catch(e => {
                callback(e); //throw e works as well
            });
        },
        (err) => {
            if (err)
                throw err;
            else {
              console.log('this is newResults:', newResults); //this is the new result as expected.
              return newResults; //this return doesn't exit the function as expected and the next log will be reached
            }
        });

        console.log('this is newResults and shouldn\'t reached at all:', newResults); //this line will be reached, but should'nt
    });
};

module.exports = module1;

app2.js

const module2 = require('./module2');
const errorHandler = require('./error'); //implements custom error handler, e.g. log, mail, prowl, etc.

module2.getSomeVals()
.then(result => {
    console.log('this is result:', result); //this returns the expected newResults array because the wrapped promise resolves the newResults
})
.catch(e => {
    errorHandler.handle(e);
});

module2.js

const async = require('async'); //node module
const db = require('./db'); //custom db class

const module2 = {};

module2.getSomeVals = () => {
    return new Promise(resolve => {

        //some asyncronous function resolves values for next chain element, e.g. database stream
        const resultFromSomeAsyncFunction = [
            {
                foo: 'foo1',
                bar: 'bar1'
            },
            {
                foo: 'foo2',
                bar: 'bar2'
            },
            {
                foo: 'foo3',
                bar: 'bar3'
            },
        ];

        resolve(resultFromSomeAsyncFunction);
    })
    .then(results => {

        let newResults = [];

        return new Promise(resolve => { 
            async.eachOf(results, (result, index, callback) => {

                return db.query("select * from names where foo = '" + result.foo + "' and bar = '" + result.foo)
                .then(rows => {

                    newResults.push(rows);
                    console.log('this is rows:', rows);
                    callback(null); //doesn't need second parameter, because newResults defined above
                })
               .catch(e => {
                    callback(e); //throw e works as well
                });
            },
            (err) => {
                if (err)
                    throw err;
                else {
                  console.log('this is newResults:', newResults); //this is the new result as expected.
                  resolve(newResults); //this resolve exit the function as expected and the next log wont be reached
                }
            });
        });

        console.log('this is newResults and shouldn\'t reached at all:', newResults); //this line wont be reached as expected
    });
};

module.exports = module2;

Module2 的代码风格很乱。我想在代码中有一个干净稳定的结构。

async eachOf 的文档说:
当所有迭代函数完成或发生错误时调用的回调。使用 (err) 调用。

返回:一个承诺,如果回调被省略

类型承诺

考虑到像resolve 这样的回调不再可用,我除了返回应该结束承诺之外。我想明白,为什么会有这样的行为。

【问题讨论】:

  • @T.J.Crowder 如果我在module2.getSomeVals() app.js 中有一个自定义捕获处理程序?
  • @T.J.Crowder 我的意思是自定义 errorHandler,而不是 catchHandler。我不想引起混乱。我更新了我的问题。谢谢!
  • 更新后的代码将拒绝转换为履行,其值为undefined。这通常是个坏主意。如果您要返回承诺链(就像您在这种情况下一样),请不要处理拒绝;让接收链的调用者处理错误。
  • @T.J.Crowder 非常感谢,但我不想浪费你的时间。你说的很清楚——我明白了。如果我拒绝一个错误,而该链中没有捕获,它将是未定义的。 throw 是这样的,不是吗?
  • 好的。然后就好了。基本上,promise 的规则之一是:您必须要么处理错误,要么将 promise 链返回到可以处理的东西。您的代码(现在)在不返回链的情况下处理错误,所以没关系。如果它返回链,你不会想要 catch 处理程序。

标签: javascript node.js asynchronous promise async.js


【解决方案1】:

在您的代码中:

        (err) => {
            if (err)
                throw err;
            else {
              console.log('this is newResults:', newResults); //this is the new result as expected.
              return newResults; //this return doesn't exit the function as expected and the next log will be reached
            }
        });

实际上,这个返回语句确实退出了函数。问题是,它无论如何都在函数的最后一行(剩下的是一些关闭}s)。下一个log 是不同执行环境的一部分。代码结构大致是

async.eachOf(results,
    successHandlingCallback,
    errorHandlingCallback /* <- your callback is inside here */ );

console.log('this is newResults and shouldn\'t reached at all:', newResults); // <- outside, no way of knowing what happened inside the callbacks

回调是一种“一劳永逸”,如果您想进一步处理结果,最好遵循 Promise 配方。

【讨论】:

  • 那么,你的意思是app2.jsmodule2.js 的设置是要走的路吗?
【解决方案2】:

由于async 版本3.0.x async.eachOf 如果省略回调,则返回promise

返回:

一个承诺,如果一个回调被省略

如果需要在 daisy-chain 中调用该函数,可以这样使用:

const async = require('async');

(function f() {
    let items = [];

    for (let i = 0; i < 10; i++) {
        items.push({
            name: 'name' + i,
            age: 20 + i
        });
    }

    let newItems = [];

    return new Promise(resolve => {

        setTimeout(() => {
            resolve(true);
        }, 500);
    })
      .then(res1 => {

          console.log('this is res1:', res1);

          return async.eachOf(items, (item, index, callback) => {

              console.log('this is item:', item);

              setTimeout(() => {
                  newItems.push({
                      name: item.name + ' Fixl',
                      age: item.age + index + 1
                  });

                  callback();

              }, 500);

          }); //<-- here is no callback function anymore. if it's that async will make a promise of this function - since v. 3.0.x
      })
      .then(res2 => {
          console.log('this is res2:', res2); //this is undefined, because async returns nothing
          console.log('this is newItems:', newItems); // this is the array with async handled values

          return newItems;
      })
      .catch(e => {
          console.error('error:', e);
      });
})();

由于这回答了原始问题,因此该答案被接受。

【讨论】:

    猜你喜欢
    • 2020-05-07
    • 2018-08-12
    • 2013-05-23
    • 2020-12-25
    • 2019-12-31
    • 2019-07-20
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    相关资源
    最近更新 更多