簡體   English   中英

forEach循環中的異步函數和回調

[英]Async function and callbacks inside a forEach loop

在這個功能里面:

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

有幾點需要注意:

  1. toReturn.push(doc)不起作用,在最后一個回調中返回一個空數組。 這是為什么?
  2. 如果出現錯誤,或者!docs為true,則永遠不會調用return callback('Error') 始終調用return callback(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)
}

腳本崩潰,因為已經調用了多個回調。 為什么每個人都這樣做以及如何避免它?

你的問題

toReturn.push(doc)不起作用,在最后一個回調中返回一個空數組。 這是為什么?

你在MongooseModel.AsyncFunction之前調用callback MongooseModel.AsyncFunction有機會執行你的function(err,doc)

如果出現錯誤,或者!docs為true,則永遠不會調用return callback('Error')。 始終調用return callback(null,toReturn)。

在安排異步功能后立即調用成功返回。 MongooseModel.AsyncFunction稍后可能會調用callback('Error')

腳本崩潰,因為已經調用了多個回調

這正是你要求的行為! 您的第二個代碼字面上說:“對於每個元素,請調用一次回調”。


一些指針

總而言之,我認為你唯一錯誤的是調用回調有點類似於從函數返回。

事實並非如此! 您的函數Example應該安排一些異步事件發生,然后立即返回(不返回任何內容),但承諾將來稍后將調用callback(error)callback(null, success)

因此,永遠不要說return callback() - 只是callback()會這樣做。 當您有一些數據要調用callback時,函數Example已經完成執行。 最終調用callback的函數將傳遞給MongooseModel.AsyncFunction的匿名函數,而不是Example本身。


怎么辦?

您可以嘗試這樣的方法:

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

發生在這里的東西:

  • 每次AsyncFunction完成時,都會聚合結果。 如果那是最后一個,你最終可以調用callback並傳遞整個數據。 (否則,我們正在等待更多功能,所以沒有callback

  • 如果在此過程中某處出現錯誤,我們會立即報告,但也要注意已經報告錯誤,以便進一步執行已安排的AsyncFunction特別沒有做任何事情。 這可以防止您的callback被調用兩次或更多次。

  • 但請注意 - toReturn的元素toReturn將是隨機的,具體取決於首先完成的異步任務。


但這很麻煩

哦,是的。 這就是為什么我們不再做回調了。 有一種稱為promises的模式使得使用異步回調意大利面更加容易,我建議在繼續前進之前先閱讀一下這個主題。

讓它變得整潔

使用promises,這種代碼看起來像:

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 { 
        // ...
    }
})

你這樣打電話:

Example([1, 2, 3]).then(
    function(results) {
        // ...
    },
    function(error) {
        // ...
    }
);

哦,這依賴於MongooseModel.AsyncFunction返回一個自己的承諾,但是Mongoose已經這樣做了,大多數庫也是如此。 如果庫沒有,您可以輕松添加promise支持(例如,參見NodeRedis )。

如何在返回之前對AsyncFunction每個結果AsyncFunction什么?

很容易!

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

想一想將被拒絕的承諾歸為類似於引發異常的東西 - 它會自然地冒出一直到Example的返回承諾。

基本上,您的函數按以下順序執行:

  1. 觸發array每個元素的異步操作
  2. 觸發所有異步調用后立即返回
  3. 異步調用返回結果,但您的函數已經返回 - 所以它們最終無處可去。

要執行多個異步調用並返回結果,您需要等待所有這些調用完成。 我通常在庫異步的幫助下解決這個問題,甚至可以讓你控制異步調用應該如何執行(串行或並行)。 例如,您可以使用異步函數數組調用async.parallel

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

您應該使用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');
        });
}

在這里,您可以閱讀有關承諾的更多信息: https//developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM