Nodejs - 错误回调时重新调用函数 - 是否存在非阻塞方式?

时间:2016-02-04 08:13:24

标签: node.js asynchronous do-while

我有一个向API发出请求的函数。有时API会出现一些小问题,并且不时有一两秒钟可用,导致出现错误,我想再次调用该函数。由于此回调后还有另外70~80行代码,我不想用if(error) <do the same stuff> else <as here>

拆分流程

经过一段时间的努力,我最终使用了一个do-while(错误)循环,它可以阻止。有这样的异步方式吗?

我的代码(简化为概括):

//This is the request part

function bar(j, callback){      
   j++;

   //simulated error
   if(j<=10){   
       return( callback('dont', j) );
   }
   //simulated success
   else{
       return( callback(null, j) );
   }

}

//This is the main function - part of a bigger piece in my code

function foo(){
   var i = 0;
   var err = 'yes'; //else we'd get an 'err is not defined'

   do{
     bar(i, function(error, j){
        i = j
        err = error;

        if(error){
          console.log(i); 
        }
        else{       
          return( console.log('done it!') );    
          // There's more here in my code
        }
     });
   } while (err);

   console.log('I blocked');
}

foo();

编辑:

对于那些感兴趣的人,这是输出:

1

2

3

4

5

6

7

8

9

10

done it!

I blocked

3 个答案:

答案 0 :(得分:9)

我建议你为你的手术做一个功能。如果失败,则设置一个短计时器,并在该计时器触发后重试。这将为您提供重试与服务器中其他代码之间的异步行为。

function requestRetry(url, data, retryTimes, retryDelay, callback) {
    var cntr = 0;

    function run() {
        // try your async operation
        request(..., function(err, data) {
            ++cntr;
            if (err) {
                if (cntr >= retryTimes) {
                    // if it fails too many times, just send the error out
                    callback(err);
                } else {
                    // try again after a delay
                    setTimeout(run, retryDelay);
                }
            } else {
                // success, send the data out
                callback(null, data);
            }
        });
    }
    // start our first request
    run();
}


requestRetry(someUrl, someData, 10, 500, function(err, data) {
    if (err) {
        // still failed after 10 retries
    } else {
        // got successful result here
    }
});

这是一个相当简单的重试方案,它只是在固定的时间间隔内重试固定的次数。更复杂的方案实现了退避算法,它们以相当快的重试开始,但是在最初的几次故障之后重试之间的较长时间后退到服务器以便更好地恢复。如果有很多客户都在进行快速重试,那么只要服务器出现打嗝,您就会遇到雪崩故障,因为所有客户突然开始快速重试,这会让您的服务更加麻烦处理所有这些请求。退避算法旨在使服务器更好地防止雪崩故障并使其更容易恢复。

如果您在服务暂停一段时间之后等待服务重新上线,那么退款方案也更合适。

答案 1 :(得分:2)

用于在出现错误时重试http调用。但首先你需要检查这个错误是否可以重试。

RETRIABLE_NETWORK_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN'];

如果RETRIABLE_NETWORK_ERRORS下的错误超出您需要重试逻辑的范围,则标记为错误。

对于重试逻辑,使用指数退避算法。你关注https://developers.google.com/api-client-library/java/google-http-java-client/backoff

const _ = require('lodash');
const request = require('request');

var httpArray = getHttpReq(); //return array

function makeHttpRequest() {
    _.each(httpArray, function (httpRequest) {
        retryRequest(httpRequest);
    });
}

function retryRequest(httpRequest) {

    const MAX_RETRY = 2;
    var retryCnt = 0;
    Retry();

    function Retry() {

        request(httpRequest, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                console.log(body)
            }
            else {
                if (retryCnt < MAX_RETRY) {
                    retryCnt += 1;
                    var currRetryIntervalMs = (1 << retryCnt) * 1000; //exponential back off logic
                    setTimeout(Retry, currRetryIntervalMs);
                }
                else {
                    console.log('http fail');
                }
            }
        });
    }
}

答案 2 :(得分:0)

这是一个异步循环

function asyncLoop(i, range, callback) {
    var results = 0;
    if(i < range) {
        // do something, update results
        aysncLoop(i+1, range, callback); 
    } else {
        callback(null, results)
    }
}

要循环10次,请按以下方式调用

asyncLoop(0, 10, function(err, results) {
    console.log(results);
});

传递您的错误条件代替范围并在循环内检查它。希望这会对你有所帮助