Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/42.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript forEach循环中的异步函数和回调_Javascript_Node.js - Fatal编程技术网

Javascript forEach循环中的异步函数和回调

Javascript forEach循环中的异步函数和回调,javascript,node.js,Javascript,Node.js,在该功能中: function Example(array, callback){ var toReturn = []; // variable 'array' has 2 elements in this case array.forEach(function(el){ MongooseModel.AsyncFunction(el, function(err, doc){ if(err || !doc){ return call

在该功能中:

function Example(array, callback){
    var toReturn = [];

    // variable 'array' has 2 elements in this case
    array.forEach(function(el){
        MongooseModel.AsyncFunction(el, function(err, doc){
            if(err || !doc){ return callback('Error'); }
            toReturn.push(doc)
        });
    });

    return callback(null, toReturn)
}
需要注意的几点:

  • toReturn.push(doc)
    不起作用,在最后一次回调时,一个空的 返回一个数组。为什么呢
  • 如果有错误,或者!docs为true时,
    返回回调('Error')
    从未被调用<代码>返回回调(null,toReturn)始终被调用
  • 另外,如果我将回调放在异步函数上方,如下所示:

    function Example(array, callback){
        var toReturn = [];
    
        // variable 'array' has 2 elements in this case
        array.forEach(function(el){
            // another callback here
            return callback('Error')
            MongooseModel.AsyncFunction(el, function(err, doc){
                if(err || !doc){ return callback('Error'); }
                toReturn.push(doc)
            });
        });
    
        return callback(null, toReturn)
    }
    
    function Example(array, callback){
        var toReturn = [];
        var previousError = false;
    
        array.forEach(function(el){
            MongooseModel.AsyncFunction(el, function(err, doc){
                if (previousError) {
                    return;
                }
                if(err || !doc) {
                    previousError = true;
                    callback('Error');
                }
                toReturn.push(doc)
                if (toReturn.length === array.length) {
                    // that was the last push - we have completed
                    callback(null, toReturn);
                }
            });
        });
    }
    
    Example([1, 2, 3]).then(
        function(results) {
            // ...
        },
        function(error) {
            // ...
        }
    );
    
    由于调用了多个回调,脚本崩溃。forEach为什么这样做?如何避免?

    您的问题 toReturn.push(doc)不工作,在最后一次回调时返回一个空数组。为什么呢

    您在
    MongooseModel之前调用
    callback
    。AsyncFunction
    有机会执行
    函数(err,doc)

    如果有错误,或者!如果docs为true,则不会调用return callback('Error')。始终调用返回回调(null,toReturn)

    在调度异步函数后,将立即调用成功返回。
    MongooseModel.AsyncFunction
    仍有可能稍后调用
    回调('Error')

    由于调用了多个回调,脚本崩溃

    这正是你要求的行为!您的第二个代码字面上说:“对于每个元素,调用回调一次”


    一些指针 总而言之,我认为您唯一的错误是调用回调有点像从函数返回

    事实并非如此!您的函数
    示例
    应该安排一些异步事件发生,然后立即返回(不返回任何内容),但承诺将来某个时候调用
    回调(错误)
    回调(null,成功)

    因此,说
    returncallback()
    ,没有任何意义——只要
    callback()
    就行了。当您有一些数据要调用
    callback
    时,函数
    Example
    将已经完成执行。最终调用
    回调
    的函数将是传递给
    MongooseModel.AsyncFunction
    的匿名函数,而不是
    示例
    本身


    现在怎么办? 您可以尝试以下方法:

    function Example(array, callback){
        var toReturn = [];
    
        // variable 'array' has 2 elements in this case
        array.forEach(function(el){
            // another callback here
            return callback('Error')
            MongooseModel.AsyncFunction(el, function(err, doc){
                if(err || !doc){ return callback('Error'); }
                toReturn.push(doc)
            });
        });
    
        return callback(null, toReturn)
    }
    
    function Example(array, callback){
        var toReturn = [];
        var previousError = false;
    
        array.forEach(function(el){
            MongooseModel.AsyncFunction(el, function(err, doc){
                if (previousError) {
                    return;
                }
                if(err || !doc) {
                    previousError = true;
                    callback('Error');
                }
                toReturn.push(doc)
                if (toReturn.length === array.length) {
                    // that was the last push - we have completed
                    callback(null, toReturn);
                }
            });
        });
    }
    
    Example([1, 2, 3]).then(
        function(results) {
            // ...
        },
        function(error) {
            // ...
        }
    );
    
    这里发生的事情:

    • 每次
      AsyncFunction
      完成时,都会聚合结果。如果这是最后一个,您最终可以调用
      callback
      并传递整个数据。(否则,我们将等待更多的函数,因此没有
      回调

    • 如果在执行过程中出现了错误,我们会立即报告,但也要注意,错误已经报告过了,因此已计划的
      asyncfonment
      的进一步执行不会做任何特别的事情。这可以防止您的
      回调
      可能被调用两次或多次

    • 不过要小心,
      toReturn
      中元素的顺序是随机的,这取决于先完成的异步任务


    但这很混乱 哦,是的。这就是为什么我们不再真正进行回调。有一种称为的模式,它使异步回调意大利面的使用变得非常简单,我建议在继续之前先阅读一下这个主题

    让我们把它弄干净 使用承诺,这种代码看起来像:

    function Example(array) {
        var promises = array.map(function(el) {
            return MongooseModel.AsyncFunction(el);
        });
        return Promise.all(promises);
    }
    
    它要短得多,而且与前面的示例一样,在错误处理或输出项排序方面没有任何问题

    用法很简单-而不是这个

    Example([1, 2, 3], function(error, results) {
        if (error !== null) {
            // ...
        } else { 
            // ...
        }
    })
    
    你这样称呼:

    function Example(array, callback){
        var toReturn = [];
    
        // variable 'array' has 2 elements in this case
        array.forEach(function(el){
            // another callback here
            return callback('Error')
            MongooseModel.AsyncFunction(el, function(err, doc){
                if(err || !doc){ return callback('Error'); }
                toReturn.push(doc)
            });
        });
    
        return callback(null, toReturn)
    }
    
    function Example(array, callback){
        var toReturn = [];
        var previousError = false;
    
        array.forEach(function(el){
            MongooseModel.AsyncFunction(el, function(err, doc){
                if (previousError) {
                    return;
                }
                if(err || !doc) {
                    previousError = true;
                    callback('Error');
                }
                toReturn.push(doc)
                if (toReturn.length === array.length) {
                    // that was the last push - we have completed
                    callback(null, toReturn);
                }
            });
        });
    }
    
    Example([1, 2, 3]).then(
        function(results) {
            // ...
        },
        function(error) {
            // ...
        }
    );
    
    哦,这依赖于
    MongooseModel.AsyncFunction
    本身返回承诺,但大多数库也是如此。如果库不支持,您可以轻松添加promise支持(参见示例)

    如何在返回
    异步函数的每个结果之前对其执行操作?

    也很容易

    function Example(array) {
        var promises = array.map(function(el) {
            return MongooseModel.AsyncFunction(el).then(function(doc) {
                return doc + "some modifications";
            });
        });
        return Promise.all(promises);
    }
    
    或者,如果您需要额外的错误处理:

    function Example(array) {
        var promises = array.map(function(el) {
            return MongooseModel.AsyncFunction(el).then(function(doc) {
                if (theresSomethingWrongWith(doc) {
                    return Promise.reject(new Error("ouch!"));
                }
                return doc + "some modifications";
            });
        });
        return Promise.all(promises);
    }
    

    将返回一个被拒绝的承诺视为类似于引发异常的事情—它自然会一直冒泡到
    示例的返回承诺

    您应该使用承诺,因为您需要等待所有异步调用完成后再返回结果。下面是一个例子:

    function Example(array, callback){
        new Promise(function(resolve, reject) {
            var toReturn = [];
    
            array.forEach(function(el){
                MongooseModel.AsyncFunction(el, function(err, doc){
                    if(err || !doc) {
                        return reject();
                    }
                    toReturn.push(doc);
                    if (toReturn.length === array.length) {
                        resolve(toReturn);
                    }
                });
            });
        })
            .then(callback)
            .catch(function() {
                callback('error');
            });
    }
    

    在这里,您可以阅读有关承诺的更多信息:

    基本上,您的函数按以下顺序执行:

  • 数组中的每个元素触发异步操作
  • 触发所有异步调用后立即返回
  • 异步调用返回结果,但您的函数已经返回了结果-因此它们不会有结果
  • 为了执行多个异步调用并返回它们的结果,您需要等待它们全部完成。我通常在库的帮助下解决这个问题,库甚至可以让您控制异步调用的执行方式(串行或并行)。例如,可以使用异步函数数组调用:

    async.parallel(array.map(function() {
      return MongooseModel.AsyncFunction(...)
    }), function(err, results) {
      // all the async calls are finished, do something with the results
    });
    

    “toReturn.push(doc)不起作用,在最后一次回调时返回一个空数组。这是为什么?”它起作用了。但是
    回调(null,toReturn)
    是在
    toReturn.push(doc)
    有机会执行之前执行的。您似乎已经知道了原因:因为在
    forEach
    回调中执行的代码是异步的。看一看,了解更多。其中一个可能会有帮助:。这不是总是拒绝承诺吗?另外,我认为应该返回承诺,而不是让函数接受回调(当然这取决于OP的情况)。更新了commentBrr,您提供了一个带有承诺的示例,这很酷,但您没有真正使用类似于
    Promise.all()
    的任何东西,这将真正简化此问题