2016-06-09 31 views
3

我正在嘗試創建一個阻塞控制流,但已經達到了死衚衕。 這個想法是,一個請求進入並流經一些處理函數。每個函數都會執行某些操作並決定是否已完成請求。如果是,則停止處理,否則調用下一個處理程序。每個處理程序可能會或可能不會異步執行某些操作(如讀取/寫入數據)。如果是這樣的話,那麼下面的處理程序在啓動之前依賴於那裏的數據。在NodeJS中實現阻塞控制流程

到目前爲止,我有兩個想法,它們都不完全符合這些要求: 1)所有的處理程序都是函數,它們被推入一個數組並重復執行some。如果處理程序希望停止控制流程,則只需返回true。 使用這種方法,我不能有任何處理程序調用異步函數。

2)所有的處理程序都是鏈接的承諾。這對我來說似乎是一個更好的主意,但我無法弄清楚如何最好地處理停止控制流。我有兩個想法: - 一旦處理程序決定中斷流程,請保留設置爲true的變量。如果需要,請在每個處理程序中檢查此值並立即解決。這意味着很多重複的代碼。 - 拒絕承諾,如果它不希望繼續正常流程,並繼續在catch。然而,使用錯誤作爲控制流程的手段的想法讓我很痛苦 - 這也意味着我必須處理由於實際錯誤而調用catch的情況。

我的spidey感覺告訴我必須有另一種方式,但我無法弄清楚。

+2

一些Promise實現,如'bluebird',提供[承諾取消](http://bluebirdjs.com/docs/api/cancellation.html)。 – robertklep

+1

基本上,你要求尋找monad。哪些承諾或多或少是。我建議[這次談話](https://fsharpforfunandprofit.com/rop/),儘管它是專門針對節點的。對於一個實際的答案,我認爲使用拒絕來阻止流量很好。沒有什麼說明它意味着「一個錯誤」,這只是一個語義約定 – Jonah

+0

編輯限制後的錯字:上面應該有專門說的_not_節點... – Jonah

回答

3

不要鏈接承諾處理程序,嵌套它們。您可以通過一組函數來編程:

var handlers = […]; // functions that can return promises 

var run = handlers.reduceRight(function(next, handler) { 
    return function() { 
     return Promise.resolve(someArg).then(handler).then(function(result) { 
      if (result) // true, whatever 
       return result; 
      else 
       return next(); 
     }); 
    }; 
}, function end() { 
    return Promise.reject(new Error("no handler matched")); 
}); 

run().then(function(result) { 
    // the result from the first handler that returned anything 
}, function(err) { 
    // some handler threw an exception, or there was no handler 
}); 
+0

我承認我很難繞過這段代碼。到目前爲止,即使顯式地返回false,我也無法獲得處理程序失敗,並且在調用每個處理程序之後,還會調用end()函數並拋出錯誤。 if語句中的結果與函數參數(res)相同嗎? –

+1

@PhilippFlenker:啊,是的,在參數名稱中輸入錯誤。當一個處理程序返回'true'時,它應該停止,當它返回'false'時,它應該繼續'next'(字面意思),當它拋出時,它會直接進入'catch'回調。 – Bergi

+0

好吧,我似乎在修改你的解決方案時犯了一個錯誤。我希望處理程序進行爭論(在所有情況下都是一樣的)。它似乎沒有參數的工作。你有關於如何使用它的參數(如處理程序(參數))的建議? –

5

您可以使用ES6生成器根據需要生成承諾。每次承諾解決時,它都可以檢查停止值,如果爲假(即繼續到下一個處理程序),請求迭代器中的下一個。

function* promiseHandlers() { 
    const handlers = [ 
    promiseHandler1, 
    promiseHandler2, 
    promiseHandler3, 
    promiseHandler4, 
    promiseHandler5 
    ]; 

    for (const handler of handlers) { 
    yield handler(); 
    } 
} 

function run(iter) { 
    const i = iter.next(); 
    if (i.done) { 
    return Promise.reject(new Error("end of iterator reached")); 
    } 
    return Promise.resolve(i.value).then(result => result || run(iter)); 
} 

run(promiseHandlers()); 

這實際上並不鏈接。相反,它會按順序執行每個promiseHandler,但這些結果不會傳遞給下一個。我認爲這對於OP所要求的是正確的,尤其是因爲真實的結果結束了迭​​代(即中斷)。

+0

確實有些事情不見了:-)我希望你不介意編輯。 – Bergi

+0

這個遞歸函數確實比我的方法好一點,因爲它更高效,不會在執行前構建整個「鏈」。這樣它也適用於開放式迭代 - 它更像是一個「while」循環,而不是'for for'循環 - 這很好地用可能是無限的生成器來描述。 – Bergi

+0

不要推出自己的協同程序!你錯過了一大堆案件。記住,如果我讓你的'run'更「符合」? –