Почему asyn c eachOf не возвращает значение в обратном вызове, а разрешает - PullRequest
0 голосов
/ 10 января 2020

Я перебираю массив js с eachOf из asny c framework и выполняю асинхронную функцию в каждой итерации. Если вызывается callback и не было выдано никакой ошибки, код достиг оператора возврата. Мне интересно, почему возвращение игнорируется и не сразу покидает цепочку обещаний. Если я оберну еще одно обещание вокруг функции eachOf и разрешу значение в функции обратного вызова, обещание первого уровня будет выполнено, как и ожидалось.

Почему это так? Разве возврата в функции обратного вызова не достаточно для завершения функции?

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 имеет стиль кода, похожий на беспорядок. Я хочу иметь чистую и стабильную структуру в коде.

Документы asyn c eachOf говорят:
Обратный вызов, который вызывается, когда все функции iteratee завершены или возникает ошибка. Вызывается с (err).

Возвращает: обещание, если обратный вызов пропущен

Тип Обещание

Учитывая, что обратного вызова, такого как resolve, больше нет, я за исключением того, что возврат должен закончиться обещанием. Я хочу понять, почему поведение ведет себя так.

Ответы [ 2 ]

0 голосов
/ 13 января 2020

Поскольку 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);
      });
})();

Так как это отвечает на Оригинальный вопрос, этот ответ принят.

0 голосов
/ 10 января 2020

В вашем коде:

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

На самом деле, этот оператор возврата действительно выходит из функции. Дело в том, что она в любом случае находится на последней строке функции (осталось несколько закрывающих } с). Следующая 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.

...