ti-enxame.com

Implementando tempos limite para retornos de chamada node.js.

Essa é uma situação típica no node.js:

asyncFunction(arguments, callback);

Quando asynFunction é concluído, callback é chamado. Um problema que vejo nesse padrão é que, se asyncFunctionnunca for concluído (e asynFunction não tiver um sistema de tempo limite interno), então callback nunca será chamado. Pior, parece que callback não tem como determinar que asynFunction nunca retornará.

Eu quero implementar um "tempo limite" pelo qual se callback não tiver sido chamado por asyncFunction dentro de 1 segundo, então callback será automaticamente chamado com a suposição de que asynFunction errou. Qual é a maneira padrão de fazer isso?

31
Randomblue

Não estou familiarizado com nenhuma biblioteca que faça isso, mas não é difícil se conectar.

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

Você provavelmente precisará sair com uma solução própria. Gostar

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

e depois

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

Você poderia fazer algo assim:

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

Uso:

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

[~ # ~] demo [~ # ~]

Mas observe o seguinte:

  • O tempo limite é iniciado imediatamente quando você chama ensureExecution, portanto não é possível armazenar em cache essa referência de função.

  • Os argumentos transmitidos para o retorno de chamada serão diferentes. Por exemplo, asyncFunction pode passar alguns argumentos para callback em caso de êxito, mas se a função for chamada pelo tempo limite, nenhum argumento será passado. Você tem que manter isso em mente. Você também pode fornecer argumentos padrão com os quais a função deve ser chamada neste caso:

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

Encontrei o mesmo problema com um script de conteúdo tentando abrir a porta na extensão BG antes que a extensão BG estivesse pronta. Uma solução foi aguardar a extensão BG responder a uma mensagem e repetir isso até obter êxito. Aqui estão os trechos de código.

Script de conteúdo:

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

Extensão 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});
    }
  });

No meu caso, normalmente demorava após o primeiro atraso de 100 ms.

1
user3417987