为什么在回调中不异步每个 returns 值而是解析

Why doesn't async eachOf returns value in callback but resolve

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

这是为什么?回调函数里的return不是应该就可以结束函数了吗?

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代码风格乱七八糟。我想在代码中有一个干净稳定的结构。

The docs of async eachOf says:
A callback which is called when all iteratee functions have finished, or an error occurs. Invoked with (err).

Returns: a promise, if a callback is omitted

Type Promise

考虑到不再有像 resolve 这样的回调可用,我认为 return 应该结束承诺。我想明白,为什么会这样。

在您的代码中:

        (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
            }
        });

实际上,这个 return 语句 确实 退出函数。问题是,它无论如何都在函数的最后一行(剩下的是一些关闭的 }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

回调是一种 "fire and forget",如果您想进一步处理结果,最好遵循 Promise 配方。

async 版本 3.0.x async.eachOf returns promise 如果省略回调。

Returns:

a promise, if a callback is omitted

如果需要在 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);
      });
})();

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