如何在Javascript中实现“函数超时” - 而不仅仅是'setTimeout'

时间:2012-01-08 15:27:43

标签: javascript timeout

如何在Javascript中实现timeout,而不是window.timeout,而是session timeoutsocket timeout - 基本上 - “function timeout

  

系统中允许的指定时间段   在指定事件发生之前,除非指定另一个事件   事件先发生;在任何一种情况下,期间终止时   任何一个事件发生。

具体来说,我想要一个javascript observing timer来观察一个函数的执行时间,如果达到或超过指定的时间,那么observing timer将停止/通知执行函数。

非常感谢任何帮助!非常感谢。

6 个答案:

答案 0 :(得分:12)

我并不完全清楚你在问什么,但我认为Javascript不能按你想要的方式工作,所以无法完成。例如,无法执行常规函数调用,直到操作完成或持续一定时间(以先到者为准)。这可以在javascript之外实现并通过javascript公开(就像使用同步ajax调用一样),但不能在具有常规函数的纯javascript中完成。

与其他语言不同,Javascript是单线程的,因此当一个函数执行时,计时器将永远不会执行(除了Web工作者,但是他们可以做的非常非常有限)。定时器只能在函数完成执行时执行。因此,您甚至无法在同步函数和计时器之间共享进度变量,因此计时器无法“检查”函数的进度。

如果你的代码是完全独立的(没有访问你的任何全局变量,没有调用你的其他函数并且无论如何也没有访问过DOM),那么你可以在web-worker中运行它(仅在较新的浏览器中可用)并在主线程中使用计时器。当web-worker代码完成时,它会向主线程发送一条消息及其结果。当主线程收到该消息时,它会停止计时器。如果计时器在接收结果之前触发,则可以终止Web工作者。但是,您的代码必须遵守网络工作者的限制。

Soemthing也可以通过异步操作完成(因为它们可以更好地与Javascript的单线程一起使用),如下所示:

  1. 启动异步操作,如ajax调用或加载图像。
  2. 使用setTimeout()启动计时器以确定超时时间。
  3. 如果计时器在异步操作完成之前触发,则停止异步操作(使用API​​取消它)。
  4. 如果异步操作在计时器触发之前完成,则取消clearTimeout()的计时器并继续。
  5. 例如,以下是如何在加载图像时设置超时:

    function loadImage(url, maxTime, data, fnSuccess, fnFail) {
        var img = new Image();
    
        var timer = setTimeout(function() {
            timer = null;
            fnFail(data, url);
        }, maxTime);
    
        img.onLoad = function() {
            if (timer) {
                clearTimeout(timer);
                fnSuccess(data, img);
            }
        }
    
        img.onAbort = img.onError = function() {
            clearTimeout(timer);
            fnFail(data, url);
        }
        img.src = url;
    }
    

答案 1 :(得分:2)

您可以在Web worker中执行代码。然后,您仍然可以在代码运行时处理超时事件。 Web工作人员完成其工作后,您可以取消超时。一旦发生超时,您就可以终止Web工作者。

execWithTimeout(function() {
    if (Math.random() < 0.5) {
        for(;;) {}
    } else {
        return 12;
    }
}, 3000, function(err, result) {
    if (err) {
        console.log('Error: ' + err.message);
    } else {
        console.log('Result: ' + result);
    }
});

function execWithTimeout(code, timeout, callback) {
    var worker = new Worker('data:text/javascript;base64,' + btoa('self.postMessage(' + String(code) + '\n());'));
    var id = setTimeout(function() {
        worker.terminate();
        callback(new Error('Timeout'));
    }, timeout);
    worker.addEventListener('error', function(e) {
        clearTimeout(id);
        callback(e);
    });
    worker.addEventListener('message', function(e) {
        clearTimeout(id);
        callback(null, e.data);
    });
}

答案 2 :(得分:1)

你只能使用一些硬核技巧来达到这个目的。例如,如果你知道你的函数返回什么类型的变量(注意每个 js函数返回一些东西,默认是undefined)你可以尝试这样的东西:define variable

var x = null;

并在单独的“线程”中运行测试:

function test(){
    if (x || x == undefined)
        console.log("Cool, my function finished the job!");
    else
        console.log("Ehh, still far from finishing!");
}
setTimeout(test, 10000);

最后运行功能:

x = myFunction(myArguments);

仅当您知道函数未返回任何值(即返回值为undefined)或返回的值始终为“not false”时才有效,即未转换为{{1 }}语句(如false0等)。

答案 3 :(得分:0)

我意识到这是一个古老的问题/话题,但这也许会对其他人有所帮助。

这是一个通用的callWithTimeout,您可以await

export function callWithTimeout(func, timeout) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => reject(new Error("timeout")), timeout)
    func().then(
      response => resolve(response),
      err => reject(new Error(err))
    ).finally(() => clearTimeout(timer))
  })
}

测试/示例:

export function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

const func1 = async () => {
  // test: func completes in time
  await sleep(100)
}

const func2 = async () => {
  // test: func does not complete in time
  await sleep(300)
}

const func3 = async () => {
  // test: func throws exception before timeout
  await sleep(100)
  throw new Error("exception in func")
}

const func4 = async () => {
  // test: func would have thrown exception but timeout occurred first
  await sleep(300)
  throw new Error("exception in func")
}

致电:

try {
  await callWithTimeout(func, 200)
  console.log("finished in time")
}
catch (err) {
  console.log(err.message)  // can be "timeout" or exception thrown by `func`
}

答案 4 :(得分:0)

My question 已被标记为该帖子的副本,所以我想我会回答它,即使原来的帖子已经九年了。

我花了一些时间来理解 Javascript 是单线程意味着什么(我仍然不确定我是否 100% 理解了事情)但这里是我如何使用 Promises 和回调。它主要基于 this tutorial

首先,我们定义一个 timeout 函数来包装 Promises:

const timeout = (prom, time, exception) => {
    let timer;
    return Promise.race([
        prom,
        new Promise((_r, rej) => timer = setTimeout(rej, time, exception))
    ]).finally(() => clearTimeout(timer));
}

这是我想超时的承诺:

const someLongRunningFunction = async () => {
  ...
  return ...;
}

最后,我是这样使用的。

const TIMEOUT = 2000;
const timeoutError = Symbol();
var value = "some default value";
try {
  value = await timeout(someLongRunningFunction(), TIMEOUT, timeoutError);
}
catch(e) {
  if (e === timeoutError) {
    console.log("Timeout");
  }
  else {
    console.log("Error: " + e);
  }
}
finally {
  return callback(value);
}

这将调用 callback 函数,返回值为 someLongRunningFunction 或超时时的默认值。您可以修改它以不同方式处理超时(例如抛出错误)。

答案 5 :(得分:-1)

observing timerexecuting function之间共享变量。

使用observing timerwindow.setTimeout实施window.setInterval。当observing timer执行时,它会将退出值设置为共享变量。

executing function不断检查变量值..如果指定了 exit ,则返回。