使用Bluebird Promises,如何用延迟反模式解决这个问题?

时间:2017-02-14 04:58:17

标签: node.js promise bluebird

我正在学习蓝鸟的承诺,我试图学习不再使用任何Deferred()。 以下代码正确运行100%并按预期运行。 但是,对于我来说,重构代码以正确使用Bluebird承诺而不是使用延迟解决方案。 我试图学习以不同的方式(正确地)思考Promise,但经过多次尝试后,我仍然无法在 > 的帮助下找出解决这个特殊问题的方法。

有没有人有想法?

以下是如何运行它:

1)在您的控制台中运行此程序。它将启动一个将使用端口8080的websocket服务器。

2)然后在另一个控制台窗口中再次运行它。在尝试使用端口8080失败后,该端口将启动并使用端口8081。

// 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
        };
}

2 个答案:

答案 0 :(得分:2)

在3年的承诺编程中,我只发现了一种使用延迟使我的代码更简单的情况。我得出的结论是,这种情况非常罕见。通过学习正确的技术(在这里使用链接),几乎总能避免它们,并最终得到更简单的代码,这些代码具有相对常见错误的风险较小(例如不完整的错误传播或未捕获的异常)。

在这种特殊情况下,您可以通过从.then()处理程序中返回新的承诺,将后续尝试链接到上一个承诺。这允许您从connect函数返回一个promise,但是保留该promise以用于将来的尝试(阻止其最终解决方案),直到将来的某些重试尝试成功或者您的重试尝试用完为止。

你可以这样做。特别尝试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的已解析值。然后,您不依赖于副作用来设置更高范围的变量。您可以从已解决的承诺中获取它,并在您知道它有效时将其存储在您想要的位置。

答案 1 :(得分:0)

这是我提出的一种可能的解决方案。你怎么看?它仍然使用总共2个promise(一个在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
    });
}