簡體   English   中英

處理錯誤后跳過承諾鏈

[英]Skipping promise chain after handling error

使用https://github.com/kriskowal/q庫,我想知道是否可以做這樣的事情:

// Module A

function moduleA_exportedFunction() {
  return promiseReturningService().then(function(serviceResults) {
    if (serviceResults.areGood) {
      // We can continue with the rest of the promise chain
    }
    else {
      performVerySpecificErrorHandling();
      // We want to skip the rest of the promise chain
    }
  });
}

// Module B

moduleA_exportedFunction()
  .then(moduleB_function)
  .then(moduleB_anotherFunction)
  .fail(function(reason) {
    // Handle the reason in a general way which is ok for module B functions
  })
  .done()
;

基本上,如果服務結果不佳,我想使用特定於模塊A內部的邏輯來處理模塊A中的故障,但仍然跳過承諾鏈中其余的模塊B功能。

跳過模塊B功能的顯而易見的解決方案是從模塊A引發錯誤/原因。但是,我將需要在模塊B中處理該錯誤/原因。理想情況下,我希望在模塊B中不需要任何額外的代碼來完成此操作那。

這很可能是不可能的:)或違反Q的某些設計原則。

在這種情況下,您會建議什么樣的替代方案?

我有兩種方法,但都有缺點:

  1. 從模塊A拋出特定錯誤,並向模塊B添加特定處理代碼:

     .fail(function(reason) { if (reason is specificError) { performVerySpecificErrorHandling(); } else { // Handle the reason in a general way which is ok for module B functions } }) 
  2. 在模塊A中執行自定義錯誤處理,然后在處理錯誤之后,拋出偽造的拒絕原因。 在模塊B中,添加條件以忽略虛假原因:

     .fail(function(reason) { if (reason is fakeReason) { // Skip handling } else { // Handle the reason in a general way which is ok for module B functions } }) 

解決方案1需要向模塊B添加模塊A特定代碼。

解決方案2解決了這個問題,但是整個偽造拒絕方法似乎非常棘手。

您能推薦其他解決方案嗎?

讓我們談談控制構造。

在JavaScript中,調用函數時,代碼以兩種方式流動。

  • 它可以向調用方return一個值,指示它已成功完成。
  • 它可能會向調用者throw錯誤,表明發生了異常操作。

看起來像:

function doSomething(){ // every function ever
  if(somethingBad) throw new Error("Error operating");
  return value; // successful completion.
}

try{
  doSomething();
  console.log("Success");
} catch (e){
  console.log("Boo");
}

諾言為這種完全相同的行為建模。

在Promises中,在.then處理程序中調用函數時,代碼以正好兩種方式流動:

  • 它可以return一個承諾或一個值,表明它已成功完成。
  • 它可能throw錯誤,表明發生了異常狀態。

看起來像:

var doSomething = Promise.method(function(){
  if(somethingBad) throw new Error("Error operating");
  return someEventualValue(); // a direct value works here too
}); // See note, in Q you'd return Q.reject()

Promise.try(function(){ // in Q that's Q().then
  doSomething();
  console.log("Success");
}).catch(function(e){
  console.log("Boo");
});

承諾控制本身的模型流程

一個promise是對概念排序操作本身的抽象。 它描述了控制如何從一個語句傳遞到另一個語句。 你可以考慮.then在一個分號的抽象。

讓我們談談同步代碼

讓我們看看您的情況下同步代碼的外觀。

function moduleA_exportedFunction() {
  var serviceResults = someSynchronousFunction();
    if (serviceResults.areGood) {
      // We can continue with the rest of our code
    }
    else {
      performVerySpecificErrorHandling();
      // We want to skip the rest of the chain
    }
}

因此,如何繼續執行我們其余的代碼只是在returning 這在同步代碼和帶有承諾的異步代碼中是相同的。 執行非常具體的錯誤處理也是可以的。

我們如何跳過同步版本中的其余代碼?

doA();
doB();
doC(); // make doD never execute and not throw an exception
doD();

好吧,即使不是立即執行,也可以通過使doC進入無限循環來使doD永遠不執行:

function doC() {
    if (!results.areGood) {
      while(true){} // an infinite loop is the synchronous analogy of not continuing
                    // a promise chain.
    }
}

因此, 可能永遠都無法解決承諾-就像其他答案所建議的-返回待處理的承諾。 但是,這是非常差的流量控制,因為意圖沒有很好地傳達給用戶,並且很可能很難調試。 想象一下以下API:

moduleA_exportedFunction-此函數發出API請求,並在數據可用時作為ServiceData對象返回服務。 否則,它將使程序進入無限循環

有點混亂,不是:)嗎? 但是,它實際上存在於某些地方。 在真正古老的API中找到以下內容並不少見。

some_bad_c_api() -此函數欺騙酒吧,一旦失敗,它將終止進程

那么,到底要終止該API中的流程,是什么困擾我們呢?

這都是關於責任的。

  • 被調用的API負責傳達API請求是否成功。
  • 呼叫者有責任決定每種情況下的處理方式。

就你而言。 ModelA只是在違反其職責范圍,它無權對程序的流程做出此類決定。 食用它的人應該做出這些決定。

更好的解決方案是拋出錯誤並讓消費者處理錯誤。 我將使用藍鳥承諾在這里,因為他們不僅是兩個數量級速度更快,並有一個更現代的API -他們也有很多更好的調試工具-在這種情況下-糖條件漁獲物和更好的堆棧跟蹤:

moduleA_exportedFunction().then(function(result){
   // this will only be reached if no error occured
   return someOtherApiCall();
}).then(function(result2){
   // this will be called if the above function returned a value that is not a 
   // rejected promise, you can keep processing here
}).catch(ApiError,function(e){
   // an error that is instanceof ApiError will reach here, you can handler all API
   // errors from the above `then`s in here. Subclass errors
}).catch(NetworkError,function(e){
   // here, let's handle network errors and not `ApiError`s, since we want to handle
   // those differently
}).then(function(){
   // here we recovered, code that went into an ApiError or NetworkError (assuming
   // those catch handlers did not throw) will reach this point.
   // Other errors will _still_ not run, we recovered successfully
}).then(function(){
   throw new Error(); // unless we explicitly add a `.catch` with no type or with 
                      // an `Error` type, no code in this chain will run anyway.
});

因此,在一行中,您將執行同步代碼中的操作,這與promise一樣。

注意Promise.method只是Bluebird用於包裝函數的便捷功能,我只是討厭同步返回Promise返回的API,因為它會造成重大破壞。

這是一種設計的東西。 通常,當模塊或服務返回承諾時,您希望它解析調用是否成功,否則失敗。 即使您知道呼叫不成功,但諾言既沒有解決也沒有失敗,基本上是一種無聲的失敗。

但是,我不知道您模塊的具體細節或原因,因此,如果您確實想在這種情況下靜默失敗,則可以通過返回未解決的承諾來做到這一點:

//模塊A

function moduleA_exportedFunction() {
  return promiseReturningService().then(function(serviceResults) {
    if (serviceResults.areGood) {
      // We can continue with the rest of the promise chain
    }
    else {
      performVerySpecificErrorHandling();
      // We want to skip the rest of the promise chain
      return q.defer().promise;
    }
  });
}

受本傑明·格魯恩鮑姆(Benjamin Gruenbaum)的評論和答案的啟發-如果我以同步代碼編寫此代碼,則將使moduleA_exportedFunction返回一個shouldContinue布爾值。

因此,有了promise,基本上就是這樣(免責聲明:這是偽代碼式的,未經測試)

// Module A

function moduleA_exportedFunction() {
  return promiseReturningService().then(function(serviceResults) {
    if (serviceResults.areGood) {
      // We can continue with the rest of the promise chain
      return true;
    }
    else {
      performVerySpecificErrorHandling();
      // We want to skip the rest of the promise chain
      return false;
    }
  });
}

// Module B

moduleA_exportedFunction()
  .then(function(shouldContinue) {
    if (shouldContinue) {
      return moduleB_promiseReturningFunction().then(moduleB_anotherFunction);
    }
  })
  .fail(function(reason) {
    // Handle the reason in a general way which is ok for module B functions
    // (And anything unhandled from module A would still get caught here)
  })
  .done()
;

它確實需要模塊B中的一些處理代碼,但是邏輯既不特定於模塊A的內部,也不涉及拋出和忽略虛假錯誤-已完成任務! :)

暫無
暫無

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

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