【问题标题】:Implementing timeouts for node.js callbacks为 node.js 回调实现超时
【发布时间】:2012-01-12 17:57:51
【问题描述】:

这是node.js中的典型情况:

asyncFunction(arguments, callback);

asynFunction 完成时,callback 被调用。我看到这种模式的一个问题是,如果asyncFunction never 完成(并且asynFunction 没有内置的超时系统),那么callback 将永远不会被调用。更糟糕的是,callback 似乎无法确定asynFunction 永远不会返回。

我想实现一个“超时”,如果callback 在1 秒内没有被asyncFunction 调用,那么callback 会自动被调用,假设asynFunction 出错了。这样做的标准方法是什么?

【问题讨论】:

  • 标准方式是调用callback 总是
  • 你怎么能保证呢?假设服务器死了...
  • @Randomblue:另一台服务器只与您的代码通信,并且负责直接调用您的回调。 asyncFunction 和实际调用 callback 的部分都在你身边,所以它应该为你做这种错误处理是合理的。

标签: javascript node.js asynchronous


【解决方案1】:

我不熟悉任何这样做的库,但自己连接起来并不难。

// Setup the timeout handler
var timeoutProtect = setTimeout(function() {

  // Clear the local timer variable, indicating the timeout has been triggered.
  timeoutProtect = null;

  // Execute the callback with an error argument.
  callback({error:'async timed out'});

}, 5000);

// Call the async function
asyncFunction(arguments, function() {

  // Proceed only if the timeout handler has not yet fired.
  if (timeoutProtect) {

    // Clear the scheduled timeout handler
    clearTimeout(timeoutProtect);

    // Run the real callback.
    callback();
  }
});

【讨论】:

  • 如果 asyncFunction 在 5000 毫秒后成功,这会调用两次回调。
  • @herby 是的,刚刚解决了这个问题:)
  • 不带参数调用回调(不过,由于这是临时的,可以逐个添加)。
  • 如果 timeoutProtect = null; 可能会出现竞争条件;在“if”评估之前不执行。
  • 它不处理异步函数中的无限循环
【解决方案2】:

你可以这样做:

function ensureExecution(func, timeout) {
    var timer, run, called = false;

    run = function() {   
        if(!called) {
            clearTimeout(timer);
            called = true;
            func.apply(this, arguments);
        }   
    };

    timer = setTimeout(run, timeout);
    return run;
}

用法:

asyncFunction(arguments, ensureExecution(callback, 1000));

DEMO

但请注意以下几点:

  • 调用ensureExecution时立即开始超时,因此无法缓存该函数引用。

  • 传递给回调的参数会有所不同。例如asyncFunction 可能会在成功时将一些参数传递给callback,但如果函数被超时调用,则不会传递任何参数。你必须牢记这一点。在这种情况下,您还可以提供调用函数的默认参数:

    function ensureExecution(func, timeout, args, this_obj) {
        // ...
        timer = setTimeout(function() {
            run.apply(this_obj, args);
        }, timeout);
        //...
    }
    

【讨论】:

  • 如果 asyncFunction 在超时后成功,这会调用两次回调。
  • 不带参数调用func
【解决方案3】:

您可能需要提出自己的解决方案。喜欢

function callBackWithATimeout (callback, timeout) {
  var run, timer;
  run = function () {
    if (timer) {
      clearTimeout(timer);
      timer = null;
      callback.apply(this, arguments);
    }
  };
  timer = setTimeout(run, timeout, "timeout");
  return run;
}

然后

asyncFunction(arguments, callBackWithATimeout(callback, 2000));

【讨论】:

    【解决方案4】:

    我在 BG 扩展准备好之前尝试打开 BG 扩展上的端口的内容脚本遇到了同样的问题。一种解决方法是等待 BG 扩展回复消息并重复此操作直到成功。这是代码sn-ps。

    内容脚本:

    var nTimes     = 10;
    var bIsReady = false;
    checkBGReady();
    function checkBGReady() {
      if (!bIsReady) {
        chrome.runtime.sendMessage({msgText: "hello "+nTimes}, function(response) {
          if (response && response.ack) {
            console.log("have response:"+response.ack+" "+nTimes);
            bIsReady = true;
            // continue with initialization
            bootStrap(sURL);
            checkReady();
          } else {
            console.log("have no ack response %o",response);
          }
        });
      }
      nTimes -= 1;
      if (nTimes > 0 && !bIsReady) {
        setTimeout(checkBGReady,100);
      }
    }
    

    BG 扩展

      chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
        console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");
        if (request.msgText) {
          console.log("Have msg "+request.msgText);
           sendResponse({ack: "have contact "+request.msgText});
        }
      });
    

    在我的情况下,它通常在前 100 毫秒延迟之后。

    【讨论】:

      猜你喜欢
      • 2014-08-31
      • 1970-01-01
      • 1970-01-01
      • 2013-11-11
      • 2016-07-30
      • 1970-01-01
      • 1970-01-01
      • 2010-10-27
      • 1970-01-01
      相关资源
      最近更新 更多