2015-12-06 54 views
0

我對JavaScript(Node.js)和Promises相當陌生。我目前正在使用AWS Lambda和DynamoDB。在Bluebird中處理多個嵌套的異步操作(promise)

我有我從異步數據庫中獲取項目列表(已promisified AWS的SDK與藍鳥的承諾API)

對於這些項目,我需要可能檢索多個子項(也異步),然後對於每個子項,我必須執行另一個異步操作並確定此異步操作是否成功。

畢竟異步操作一個項目完成(即所有異步操作的子項成功或失敗),我需要更新的項目在DB的狀態(失敗/成功)。

這(下)是我到目前爲止。你能告訴我,如果我做得對嗎?它有沒有邏輯錯誤?可以改進嗎?

var doc = require('dynamodb-doc'); 
var promise = require("bluebird"); 

var dynamodb = new doc.DynamoDB(); 

var params = { /* ... */ }; 

promise.promisifyAll(Object.getPrototypeOf(dynamodb)); 

dynamodb.queryAsync(params) 
    .then(function(data) { 
     var items = data.Items; 

     var promises = items.map(function(item) { 
      params = { /* ...*/ }; 

      return dynamodb.queryAsync(params) 
       .then(function(data2) { 
        var childItems = data2.Items; 

        var morePromises = childItems.map(function(device) { 
         return doAsyncWork() 
          .then(function success() { 
           console.log("Success!"); 
          }) 
          .catch(function error(err) { 
           console.log("Error!"); 
          }) 
        }); 

        return promise.all(morePromises); 
       }) 
       .then(function() { 
        // Update status for item in DB 
        params = { /* ...*/ }; 
        return dynamodb.updateItemAsync(params); 
       }); 
     }); 

     return promise.all(promises); 
    }) 
    .then(function() { 
     var response = { /* ... */ }; 

     context.succeed(response); 
    }) 
    .catch(function(err) { 
     context.fail(err); 
    }); 

一些其他的事情:

對於一個項目的所有異步操作完成我使用Promise.all(),並從文檔我可以看到,如果連一個承諾得到了拒絕後續承諾也會被拒絕。我不希望發生這種情況,即使單個承諾被拒絕,我也希望它繼續下去。

同樣對於我在最後使用Promise.all()來等待所有項目完成其處理的所有項目。如果一個失敗了,其餘的將不會被處理,對吧?我該如何克服這一點?

有很多嵌套,我怎麼可以改進這個代碼?

我需要一種整合所有結果的方法,並將其作爲響應例如是這樣的:

{ 
    "Results": [{ 
     "ItemId": " ... ", 
     "Status": "Success", 
     "ChildItems": [{ 
      "ChildItemId": " ... ", 
      "Status": "Success" 
       /* ... 
       ... 
       ... 
      */ 
     }] 
    }, { 
     "ItemId": " ... ", 
     "Status": "Error", 
     "ChildItems": [{ 
      "ChildItemId": " ... ", 
      "Status": "Success" 
     }, { 
      "ChildItemId": " ... ", 
      "Status": "Error" 
     }] 
    }] 
} 

一個解決方案(可能是一種醜陋的),這使我心裏是有一個全局對象外,然後在其內存儲結果。有沒有其他優雅的方式來做到這一點?

謝謝。

回答

1

可以改進嗎?

有些事情可以改善,因爲你使用的是藍鳥。

使用藍鳥的promise.map()保存代碼:

而不是array.map()其次promise.all(),你可以使用藍鳥的promise.map()通過改變這樣的代碼:

   var childItems = data2.Items; 

       var morePromises = childItems.map(function(device) { 
        return doAsyncWork() 
         .then(function success() { 
          console.log("Success!"); 
         }) 
         .catch(function error(err) { 
          console.log("Error!"); 
         }) 
       }); 

       return promise.all(morePromises); 

這樣:

   return promise.map(data2.Items, function(item) { 
        // do something with item 
        return doAsyncWork(); 
       }); 

小心.catch()剛剛登錄的處理程序。

如果您處理一個承諾拒絕,並且不重新拋出或返回拒絕承諾,承諾國家將拒絕從滿足到改變。

所以,當你使用一個.catch()像你這樣在這裏:

 return dynamodb.queryAsync(params) 
      .then(function(data2) { 
       var childItems = data2.Items; 

       var morePromises = childItems.map(function(device) { 
        return doAsyncWork() 
         .then(function success() { 
          console.log("Success!"); 
         }) 
         .catch(function error(err) { 
          console.log("Error!"); 
         }) 
       }); 

       return promise.all(morePromises); 
      }) 

這將「吃」的是來自從doAsyncWork()一個拒絕承諾任何錯誤。偶爾,這就是你想要的(你想要處理錯誤並繼續,就像沒有錯誤),但很多時候你需要錯誤的傳播回來。您可以登錄,但讓錯誤被重新拋出它傳播:

 return dynamodb.queryAsync(params) 
      .then(function(data2) { 
       var childItems = data2.Items; 

       var morePromises = childItems.map(function(device) { 
        return doAsyncWork() 
         .then(function success() { 
          console.log("Success!"); 
         }) 
         .catch(function error(err) { 
          console.log("doAsyncWork Error: ", err); 
          //rethrow so error propagates 
          throw err; 
         }) 
       }); 

       return promise.all(morePromises); 
      }) 

對於一個項目的所有異步操作完成我使用 Promise.all(),並從文檔,我可以看到如果即使一個 答應被拒絕,後續的承諾也會被拒絕爲 。我不希望發生這種情況,即使單個承諾被拒絕,我也希望它繼續。

在藍鳥,如果你不想Promise.all()當一個承諾拒絕中止,你可以,如果你使用2.x版本青鳥使用Promise.settle()代替Promise.all()如果您使用的是Bluebird 3.x,那麼在您回覆您的承諾時使用.reflect()。如何做到這一點解釋here in the Bluebirds docs。就我個人而言,我喜歡Promise.settle()的工作方式,但一定有一些標準方向的理由來改變它。

有很多嵌套,我怎麼可以改進這個代碼?

你可以鏈接你正在做的一些事情而不是嵌套。請參閱How to chain and share prior results with Promises以獲取排序多個操作的各種方法,而不需要太多的嵌套和累積結果。