【问题标题】:Recursive promise failing递归承诺失败
【发布时间】:2017-11-01 04:05:26
【问题描述】:

背景

我正在尝试使用来自 Node.js 的 net.Socket 连接到机器。有时连接正常,我成功了,有时连接断开,所以我不断重试。

目标

我想写一个reconnect 方法,尝试连接到一个IP。当它成功时,它会解决,但如果它失败,它应该继续尝试。

代码

    const reconnect = function ( connectOpts ) {
        return new Promise( resolve => {
            connect( connectOpts )
                .then( () => {
                    console.log( "CONNECTED " );
                    resolve();
                } )
                .catch( () => {
                    console.log( "RETRYING" );
                    connect( connectOpts );
                } );
        } );
    };

    const connect = function ( connectOpts ) {
        return new Promise( ( resolve, reject ) => {
            socket = net.createConnection( connectOpts );

            //once I receive the 1st bytes of data, I resolve();
            socket.once( "data", () => {
                resolve();
            } );

            socket.once( "error", err => reject( err ) );
        } );
    };

reconnect({host: localhost, port: 8080})
    .then(() => console.log("I made it!"));

问题

问题是当连接失败时,它只尝试了一次,然后就炸了!

问题

如何更改我的代码,以便在每次失败时都能重新连接? PS:重要的是此功能是异步!

【问题讨论】:

    标签: javascript node.js sockets


    【解决方案1】:

    我的解决方案

    在阅读了所有其他解决方案后,我最终选择了这个:

       const reconnect = async function ( connectOpts ) {
            let done = false;
            while ( !done ) {
                done = await connect( connectOpts )
                    .then( () => true )
                    .catch( () => false );
            }
        };
    
    const connect = function ( connectOpts ) {
            return new Promise( ( resolve, reject ) => {
                socket = net.createConnection( connectOpts );
    
                //once I receive the 1st bytes of data, I resolve();
                socket.once( "data", () => {
                    resolve();
                } );
    
                socket.once( "error", err => reject( err ) );
            } );
        };
    
    reconnect({host: localhost, port: 8080})
        .then(() => console.log("I made it!"));
    

    为什么?

    1. 生成器太过分了。生成器可能有一个有效的用例,但不是在这里。使用它们会使principle of least astonishment 失效,而同样可以通过更简单的方式实现。
    2. 延迟承诺是众所周知的anti-pattern。不,不。
    3. 使用 setImmediate 是非常不必要的。与其要求循环执行立即运行代码,不如在大多数情况下(如本例)使用循环。
    4. 调用 reconnect 而不是 connect 将使调用方失去与 reconnect 调用的连接。当重新连接最终成功时,它将是一个不同的,并且不会通知原始请求的调用者。

    我的解决方案解决了所有这些问题。

    但是,你的解决方案是阻塞的!

    不,不是。在任何其他语言中都可以,但是在这里,由于我使用的是await 运算符,因此我实际上允许Node 继续执行循环。就像一个承诺。

    【讨论】:

      【解决方案2】:

      查看代码,我发现了这个。

      • 无论resolve 还是reject,重试都不会命中。然后UnhandledPromiseRejectionWarning 可能会发生。

      然后让我们在.catch 处理程序中调用reconnect 本身。解决后,致电resolve() 解决 Promise。

          const reconnect = function ( connectOpts ) {
              return new Promise( resolve => {
                  connect( connectOpts )
                  .then( () => {
                      console.log( "CONNECTED " );
                      resolve();
                  } )
                  .catch( () => {
                      console.log( "RETRYING" );
      
                      // connect( connectOpts );
      
                      return reconnect( connectOpts )
                      .then(() => {
                          resolve();
                      });
      
                  } );
              } );
          };
      

      【讨论】:

        【解决方案3】:

        试试这个:

        const connect = function(connectOpts) {
        
          const connectResolver = (resolve) => {
            socket = net.createConnection(connectOpts);
            socket.once("data", resolve);
            socket.once("error", () => 
              // you can try setTimeout() here with specific retry in milliseconds here
              // say like setTimeout(() => connectResolver(resolve), 200);
              // instead of immediate retry in the end of every event loop
              // up to you
              setImmediate(() => connectResolver(resolve)));
          }
        
          return new Promise(connectResolver);
        };
        
        connect({
            host: localhost,
            port: 8080
          })
          .then(() => console.log("I made it!"));
        

        【讨论】:

          【解决方案4】:

          正如您正确指出的那样,当您尝试从 .catch() 方法进行连接时,代码将会崩溃。

          .catch( () => {
                          console.log( "RETRYING" );
                          connect( connectOpts );
                      } );
          

          有两种方法可以解决这个问题

          选项 1:使用 ES6 生成器函数

          我最近遇到了类似的问题,我使用生成器函数来解决我的问题。本质上,生成器函数允许您暂停执行,直到异步任务完成。然后您可以根据异步任务的结果继续执行

          参考-https://eladnava.com/write-synchronous-node-js-code-with-es6-generators/

          选项 2 - 使用延迟承诺

          对于这个用例,您可以使用称为延迟承诺的东西。更多细节在这里- https://mostafa-samir.github.io/async-recursive-patterns-pt2/

          另外,这里有一个类似的 stackoverlow 问题- execute promises recursively nodejs

          希望有帮助!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-02-04
            • 1970-01-01
            • 2018-05-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-03-23
            相关资源
            最近更新 更多