2017-02-14 25 views
1

我正在學習藍鳥承諾,我試圖學會不再使用任何延期()。 下面的代碼正確且按預期運行100%。 但是,這裏對我來說是一個練習,重構代碼以正確使用Bluebird promise,而不是使用Deferred解決方案。 我試圖學會對承諾進行不同的(正確的)思考,但經過多次嘗試後,我仍然無法弄清楚如何在下面解決這個問題,而不是在延期幫助下。使用藍鳥承諾,如何解決與延期的反模式?

有沒有人有想法?

以下是如何運行它:

1)在控制檯中運行該程序。它將啓動將使用端口8080的websocket服務器。

2)然後在另一個控制檯窗口中再次運行它。其中一個將啓動並使用端口8081後3端口8080

// Initialization stuff 
const WebSocket = require('ws'); 
var wsServer; 

// Main Program 
// ================================================================= 
tryCreateWebsocket().then(
    function(){ 
     console.log("Websocket succesfully initialized."); 
    }, 
    function(){ 
     console.log("Websocket startup has failed!"); 
    } 
); 
// ================================================================= 



// Helper function: Creating a websocket, with a port as parameter 
function createWebsocket(port){ 
    return new Promise(function(resolve, reject){ 

     wsServer = new WebSocket.Server({ 
      perMessageDeflate: false, 
      port: port 
     }); 

     wsServer.on("error", reject); 
     wsServer.on("listening", resolve); 
    }); 
} 


// Main function: I try to create a websocket on 5 different ports with a resursive function 
function tryCreateWebsocket(attempt, myMainDfd){ 

    if(typeof attempt === "undefined"){ 
     attempt = 1; 
     myMainDfd = deferred(); 
    } 

    var ports = [8080, 8080, 8080, 8081, 8082]; // In the 2nd client, this should fail until port 8081 
    var curPort = ports[attempt - 1]; 

    var maxAttempts = 5; 


    createWebsocket(curPort) 
     .then(
      function(){ 
       myMainDfd.resolve(); // Success 
      }, 
      function(err){ // Error, retry 
       if(attempt != maxAttempts){ 
        console.log("- attempt " + attempt + " failed. Retry"); 
        tryCreateWebsocket(++attempt, myMainDfd); 
       }else{ 
        myMainDfd.reject(); 
       } 
      } 
     ); 

    return myMainDfd.promise; 
} 


// Helper Function: I'm still using deferreds for now 
function deferred() { 
     var resolve, reject; 
     var promise = new Promise(function() { 
       resolve = arguments[0]; 
       reject = arguments[1]; 
     }); 
     return { 
       resolve: resolve, 
       reject: reject, 
       promise: promise 
     }; 
} 
+0

會像[這個小提琴](https://jsfiddle.net/pk50ks04/)的工作? –

+0

@JaromandaX - 我剛剛注意到你的評論。它似乎沒有工作,但儘管它看起來真的很短,整齊,我會很難找出一個:-) –

+0

對不起 - 我犯了兩個小錯誤 –

回答

2

失敗的嘗試,在3年的承諾編程的,我只發現了一個情況下使用延遲使我的代碼更簡單。我得出結論,這是一個非常罕見的情況。通過學習正確的技術(在這裏使用鏈接),幾乎可以避免它們,最終得到更簡單的代碼,這些代碼很少出現相當常見的錯誤(例如不完整的錯誤傳播或未捕獲的異常)。

在這種特殊情況下,您可以通過從.then()處理程序中返回新的承諾,將後續嘗試鏈接到以前的承諾。這允許您從連接功能中返回一個承諾,但保留這個承諾用於未來的嘗試(保留其最終解決方案),直到將來某個重試嘗試成功完成,或者直到您嘗試重試爲止。

你可以這樣做。特別嘗試在connect()函數內發生的事情。

function tryCreateWebsocket(){ 

    var attempt = 1; 
    var ports = [8080, 8080, 8080, 8081, 8082]; 
    var maxAttempts = ports.length; 

    function connect() { 
     var curPort = ports[attempt - 1]; 
     return createWebsocket(curPort).catch(function(err){ // Error, retry 
      if(attempt < maxAttempts){ 
       console.log("- attempt " + attempt + " failed. Retry"); 
       ++attempt; 

       // chain next attempt onto previous promise 
       return connect(); 
      } else { 
       // reject here with no more retries 
       throw new Error("max retry attempts exceeded without successful connection"); 
      } 
     }); 
    } 

    // start trying to connect, return a promise 
    // errors will be caught and subsequent retries will be chained 
    // onto this first promise until it either succeeds or runs out 
    // of retry attempts 
    return connect(); 
} 

// Main Program 
// ================================================================= 
tryCreateWebsocket().then(function(wsServer){ 
    console.log("Websocket succesfully initialized."); 
    // server instance is valid here, use it for further code 
},function(){ 
    console.log("Websocket startup has failed!"); 
}); 
// ================================================================= 



// Helper function: Creating a websocket, with a port as parameter 
function createWebsocket(port){ 
    return new Promise(function(resolve, reject){ 

     wsServer = new WebSocket.Server({ 
      perMessageDeflate: false, 
      port: port 
     }); 

     wsServer.on("error", reject); 
     wsServer.on("listening", function() { 
      resolve(wsServer); 
     }); 
    }); 
} 

請注意,我更改了設計以使wsServer實例返回的promise的解析值。然後,您不依賴副作用來設置更高範圍的變量。你可以從解決的承諾中獲得它,並在你知道它有效的時候將它存儲在你想要的地方。

+0

謝謝,我在這裏學習了很多東西。我之前在投擲錯誤方面掙扎不已,而且我已經離開了這條路。但現在我明白你是如何做到的。我已經將你的代碼的原理添加到了我的現在。 –

+0

我認爲現在很清楚。這一切都很好,我學到了一些關鍵原則。我已經將解決​​方案標記爲答案,我想這就是它的正確方法。(這是我多年潛伏後第一次在這個網站上發佈了一些東西) Thx很多爲您的建議! –

+0

@johndoe - 是的,就是這樣。樂意效勞。 – jfriend00

0

這是我想出的一種可能的解決方案。你怎麼看?它仍然使用總共2個承諾(一個在createWebsocket函數中,另一個在下面的tryCreateWebsocket函數中)。

function tryCreateWebsocket(){ 

    var lstPorts = [8080, 8080, 8080, 8081, 8080]; 
    return new Promise(function(resolve, reject){ 

     function next(port) { 

      createWebsocket(port) 
       .then(
        resolve, 
        function(){ // Reject, but not until you've tried a little 
         console.log("Port "+port+" failed. I might try the next port."); 
         // rejected 
         if(lstPorts.length >= 1){ 
          next(lstPorts.shift()) 
         }else{ 
          reject(); // do reject 
         } 

        } 
       ); 
     } 

     next(lstPorts.shift()); // Start the loop 
    }); 
} 
+0

這通常被認爲是[承諾反模式](https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns),您不必要地包裝了您可能剛剛在另一個手動創建的承諾中返回的承諾。當這樣做在錯誤處理或錯誤傳播中犯錯時相當容易。僅供參考,任何時候你看到'。然後(解決)'你可能只是回覆了承諾,並避免將它包裝在一個額外的承諾。這總是一個信號,可能有更好的方法。你可以看到我在答案中如何使用鏈接,以避免將它全部包裝在另一個承諾中。 – jfriend00