javascript 为 node.js 回调实现超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8377777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 03:15:37  来源:igfitidea点击:

Implementing timeouts for node.js callbacks

javascriptnode.jsasynchronous

提问by Randomblue

This is a typical situation in node.js:

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

asyncFunction(arguments, callback);

When asynFunctioncompletes, callbackgets called. A problem I see with this pattern is that, if asyncFunctionnevercompletes (and asynFunctiondoesn't have a built-in time-out system) then callbackwill never be called. Worse, it seems that callbackhas no way of determining that asynFunctionwill never return.

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

I want to implement a "timeout" whereby if callbackhas not been called by asyncFunctionwithin 1 second, then callbackautomatically gets called with the assumption that asynFunctionhas errored out. What is the standard way of doing this?

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

采纳答案by Alex Wayne

I'm not familiar with any libraries that do this, but it's not hard to wire up yourself.

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

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

回答by Alex Wayne

You probably need to come out with a solution of your own. Like

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

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

and then

接着

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

回答by Felix Kling

You could do something like this:

你可以这样做:

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

Usage:

用法:

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

DEMO

演示

But note the following:

但请注意以下几点:

  • The timeout is started immediately when you call ensureExecution, so you cannot cache that function reference.

  • The arguments passed to the callback will differ. For example asyncFunctionmight pass some arguments to callbackupon success, but if the function is called by the timeout, no arguments will be passed. You have to keep that it mind. You could also provide default arguments with which the function should be called in this case:

    function ensureExecution(func, timeout, args, this_obj) {
        // ...
        timer = setTimeout(function() {
            run.apply(this_obj, args);
        }, timeout);
        //...
    }
    
  • 调用时会立即开始超时ensureExecution,因此您无法缓存该函数引用。

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

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

回答by user3417987

I ran into the same problem with a content script trying to open the port on the BG extension before the BG extension was ready. A work around was to wait for the BG extension to reply to a message and repeat this till successful. Here are the code snippets.

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

Content Script:

内容脚本:

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 Extension

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

In my case it usually took after the first 100ms delay.

就我而言,它通常在第一个 100 毫秒延迟之后进行。