2017-02-14 2 views
1

Bluebird 약속을 배우고 있으며 더 이상 Deferred()를 사용하지 않는 법을 배우려고합니다. 아래의 코드는 올바르게 100 % 실행됩니다. 그러나 여기에는 Deferred 솔루션을 사용하는 대신 Bluebird 약속을 사용하도록 코드 리팩토링에 대한 연습 문제가 나와 있습니다. 약속에 대해 다르게 (올바르게) 생각하는 법을 배우려하지만, 많은 시도를 거친 후에 아래의이 특정 문제를 해결하는 방법을 알아낼 수 없으며 없이는 Deferreds의 도움을받을 수 있습니다.Bluebird Promises를 사용하여 연기 방지 패턴으로이를 해결하는 방법은 무엇입니까?

누구나 아이디어가 있습니까?

1) 콘솔에서이 프로그램을 실행 : 여기

그것을 실행하는 방법입니다. 포트 8080을 사용할 웹 소켓 서버를 부팅합니다.

2) 그런 다음 다른 콘솔 창에서 두 번째로 실행하십시오. A는 내 코드는 간단하게 지연된 사용하는 곳 하나가 부팅 3가 약속 프로그래밍의 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

[this fiddle] (https://jsfiddle.net/pk50ks04/)와 같은 작품이 좋습니까? –

+0

@ JaromandaX - 지금 당신의 의견을 알아 챘습니다. 그것은 작동하지 않는 것 같지만 실제로는 짧고 깔끔하게 보입니다. 나는 그 중 하나를 알아내는 데 어려움을 겪습니다 :-) –

+0

그게 미안 해요 - 두 번의 작은 실수를했습니다 –

답변

2

으로 시도 실패 후 포트 8081를 사용합니다, 나는 단 하나 개의 상황을 발견했습니다. 나는 그것이 매우 드문 상황이라는 결론에 도달했습니다. 올바른 기술을 배우면 (여기에서 체인을 사용하여) 거의 항상 오류를 피할 수 있고 오류가 전파되지 않거나 잡히지 않는 예외와 같이 일반적인 오류의 위험이 적은 간단한 코드로 끝납니다.

이 경우에는 .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 인스턴스가 반환 된 약속의 해결 된 값이되도록 디자인을 변경했습니다. 그런 다음 더 높은 범위 변수를 설정하기 위해 부작용에 의존하지 않습니다. 해결 된 약속에서 가져 와서 그것이 유효하다는 것을 알고있는 시점에 원하는 곳에 저장할 수 있습니다.

+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

이것은 일반적으로 수동으로 작성한 다른 약속으로 반환 할 수있는 약속을 불필요하게 감쌀 수있는 [promise anti-pattern] (https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns)으로 간주됩니다. 오류 처리 또는 오류 전파에서 실수를하기 위해이 작업을 수행하는 것은 상당히 쉽습니다. FYI 언제든지'.then (resolve)'를 보았을 때, 아마도 약속을 되 돌리고 추가 약속을하는 것을 피할 수 있었을 것입니다. 항상 좋은 방법이 있다는 신호입니다. 내 답변에서 체인을 사용하여 다른 약속을 모두 포기하는 것을 피할 수 있습니다. – jfriend00

관련 문제