簡體   English   中英

Node.js:你如何處理循環中的回調?

[英]Node.js: How do you handle callbacks in a loop?

我正在使用Node.js和Box SDK。 我的(失敗!)代碼如下所示:

var connection = box.getConnection(req.user.login);
connection.ready(function () {
  connection.getFolderItems(0, null, function (err, result) {
    if (err) {
      opts.body = err;
    } else {
      opts.body = result;
      var a = [];
      for (var i=0; i < result.entries.length; i++) {
        connection.getFileInfo(result.entries[i].id, function (err, fileInfo) {
        if (err) {
          opts.body = err;
        } else {
          a.push(fileInfo);
        }
      });}
    }

在“程序”術語中,這是我想要做的:

var connection= box.getConnection()
var items = connection.getFolderItems()
var itemList = new List
foreach (item in items) {
  connection.getFileInfo(item.id)
  itemList.add(item)
}
display(itemList)

我的問題是connection.getFolderItems()connection.getFileInfo()是異步的 - 在返回所有結果之前,“for”循環退出。

問:Node.js中最好的方法是1)獲得第一個異步調用的結果,2)遍歷列表,進行更多的異步調用,以及3)當所有內容都“完成”時處理結果。

問: 承諾在這里是一個不錯的選擇嗎?

問: 完成()/ next()是一個選項嗎?

問:對於這種情況,Node.js中是否有任何“標准習慣用法”?

Promise是一個好主意,但你可能想看看async模塊,特別是集合處理程序。 它允許您針對“事物”列表運行異步調用,並在完成所有異步調用時為您提供運行方法的位置。 不知道這是否比承諾更好 ,但選擇總是很好。

// Should give you a rough idea
async.each(items, function (item, callback) {
  connection.getFileInfo(result, callback);
}, function (err) {
  console.log('All done');
});

https://github.com/caolan/async#each

問:Node.js中最好的方法是1)獲得第一個異步調用的結果,2)遍歷列表,進行更多的異步調用,以及3)當所有內容都“完成”時處理結果。

有多種方法。 手動編碼,Promises,異步庫。 “最好的”是旁觀者的眼睛,所以我們不能在這里說。 我使用Promises進行所有異步編碼。 它們已在ES6中正式標准化,並且有良好,強大的實現(我喜歡Bluebird的超出標准的額外功能,簡化了復雜的異步任務,以及它的promisifyAll()功能,它為任何標准的異步操作提供了一個promise接口使用異步回調調用約定)。

我建議不要手工編寫復雜的異步操作,因為強大的錯誤處理非常困難,並且可以在異步回調中靜默地使用異常,從而導致錯誤處理丟失和調試困難。 Async庫可能是最好的非Promise方式,因為它提供了圍繞異步回調的一些基礎架構和同步功能。

我個人更喜歡承諾。 我認為隨着時間的推移,我們會看到更多異步API標准化返回承諾,因此我認為這是一種更好的選擇,可以用於前瞻性的學習和編程方式。

問:承諾在這里是一個不錯的選擇嗎?

是的,promises允許你運行一堆異步操作,然后使用像Promise.all()這樣的東西來知道它們什么時候完成。 它還將為您收集所有異步操作的所有結果。

問:完成()/ next()是一個選項嗎?

我不確定你在這里問的是什么,但你可以手動編寫異步操作代碼並行運行並知道它們何時完成或按順序運行它們並知道它們何時完成。 Promise為你做了很多這項工作,但你可以不用它們手動完成。

問:對於這種情況,Node.js中是否有任何“標准習慣用法”?

如果使用promises,會有一種常見的方法來執行此操作。 如果不使用promises,可能沒有“標准習語”,因為有很多不同的方法可以自己編寫代碼。

承諾實施

這是使用node.js中的Bluebird Promise庫的示例:

var Promise = require('bluebird');
var connection = Promise.promisifyAll(box.getConnection(req.user.login));
connection.ready(function() {
    connection.getFolderItemsAsync(0, null).then(function(result) {
        return Promise.map(result.entries, function(item) {
            return connection.getFileInfoAsync(item.id);
        })
    }).then(function(results) {
        // array of results here
    }, function(err) {
        // error here
    });
});

這是如何工作的:

  1. Promisify連接對象,以便其所有方法都有一個返回promise的版本(只需在方法的末尾添加“Async”以調用此promisified版本)。

  2. 調用getFolderItemsAsync()並使用result.entries數組解析其promise

  3. 運行該數組的映射,並行運行所有操作並返回一個promise,當完成所有操作后,該promise將使用一系列有序結果進行解析。

  4. 每個條目的實際結果都是通過connection.getFileInfoAsync()實現的。

  5. 創建解析處理程序和拒絕處理程序。 如果進程中的任何位置發生任何錯誤,它將傳播到拒絕處理程序。 如果所有操作都成功,將使用有序的結果數組調用最后一個解析處理程序。

如果出現錯誤,則上述版本將中止,除了錯誤代碼之外,您沒有得到任何結果。 如果您想在出現錯誤時繼續使用null結果,那么您可以使用以下內容:

var Promise = require('bluebird');
var connection = Promise.promisifyAll(box.getConnection(req.user.login));
connection.ready(function() {
    connection.getFolderItemsAsync(0, null).then(function(result) {
        return Promise.map(result.entries, function(item) {
            return connection.getFileInfoAsync(item.id).catch(function(err){
                // post the results as null and continue with the other results
                return null;
            });
        })
    }).then(function(results) {
        // array of results here (some might be null if they had an error)
    }, function(err) {
        // error here
    });
});

手動編碼版本

這是一個手動編碼的版本。 這個的關鍵是通過比較if (results.length === result.entries.length)來檢測異步循環何時完成。 注意:這有不完整的錯誤處理,這是手動編碼和不使用像promises這樣的異步框架的困難之一。

var connection = box.getConnection(req.user.login);
connection.ready(function () {
    connection.getFolderItems(0, null, function (err, result) {
        if (err) {
            // do error handling here
            opts.body = err;
        } else {
            var results = [];
            for (var i = 0; i < result.entries.length; i++) {
                connection.getFileInfo(result.entries[i].id, function (err, fileInfo) {
                    if (err) {
                        // do error handling here
                        opts.body = err;
                        results.push(null);
                    } else {
                        results.push(fileInfo);
                    }
                    // if done with all requests
                    if (results.length === result.entries.length) {
                        // done with everything, results are in results
                        // process final results here
                    }
                });
            }
        }
    });
});

問:Node.js中最好的方法是1)獲得第一個異步調用的結果,2)遍歷列表,進行更多的異步調用,以及3)當所有內容都“完成”時處理結果。

您既可以使用異步庫,也可以使用promise來調用函數調用,而是使用Promise。 兩者都很容易使用。

問:承諾在這里是一個不錯的選擇嗎?

是。 但它要求您在使用之前首先宣傳您的方法。

問:完成()/ next()是一個選項嗎?

據我所知,這是一個完全不同的概念。 這里done是指一個回調函數,您可以在方法完成后調用它。 next通常用於快遞以傳遞對下一條路線的請求,我認為這與您提出的問題無關。

問:對於這種情況,Node.js中是否有任何“標准習慣用法”?

它通常被稱為“異步”或“非阻塞”調用

暫無
暫無

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

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