如何实现“;功能超时”;在Javascript中-不仅仅是'setTimeout'
How to implement a "function timeout" in Javascript - not just the 'setTimeout'
如何在Javascript中实现超时,不是window.timeout
,而是类似session timeout
或socket timeout
的东西-基本上是"function timeout
"
系统中允许经过的指定时间段在发生指定事件之前,除非另有指定事件首先发生;无论哪种情况,当任何一个事件都会发生。
具体来说,我想要一个javascript observing timer
,它将观察函数的执行时间,如果达到或超过指定时间,observing timer
将停止/通知正在执行的函数。
非常感谢您的帮助!非常感谢。
我不完全清楚你在问什么,但我认为Javascript不能按照你想要的方式工作,所以无法完成。例如,不能让常规函数调用持续到操作完成或某一时间段(以先到者为准)。这可以在javascript之外实现,并通过javascript公开(就像同步ajax调用一样),但不能在具有常规函数的纯javascript中实现。
与其他语言不同,Javascript是单线程的,因此在执行函数时,计时器永远不会执行(除了网络工作者,但他们的能力非常有限)。计时器只能在函数执行完毕时执行。因此,您甚至不能在同步函数和计时器之间共享进度变量,因此计时器无法"检查"函数的进度。
如果您的代码是完全独立的(没有访问任何全局变量,没有调用其他函数,也没有访问DOM),那么您可以在web工作程序中运行它(仅在较新的浏览器中可用),并在主线程中使用计时器。当web工作程序代码完成时,它会向主线程发送一条消息,其中包含它的结果。当主线程接收到该消息时,它会停止计时器。如果计时器在收到结果之前启动,它可能会杀死web工作人员。但是,您的代码将不得不接受网络工作者的限制。
Soemthing也可以用异步操作来完成(因为它们与Javascript的单线程性更好地配合),如下所示:
- 启动异步操作,如ajax调用或加载图像
- 使用
setTimeout()
启动计时器作为超时时间 - 如果计时器在异步操作完成之前触发,则停止异步操作(使用API取消它)
- 如果异步操作在计时器触发之前完成,则使用
clearTimeout()
取消计时器并继续
例如,以下是如何在加载图像时设置超时:
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;
}
我的问题被标记为这个问题的副本,所以我想我会回答它,尽管原来的帖子已经九年了。
我花了一段时间才明白Javascript是单线程的(我仍然不确定我是否100%理解),但以下是我如何使用Promises和回调解决类似用例的。它主要基于本教程。
首先,我们定义了一个timeout
函数来包装Promise:
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);
}
这将使用返回值someLongRunningFunction
或超时时的默认值调用callback
函数。您可以修改它以不同的方式处理超时(例如抛出错误)。
您可以在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);
});
}
我意识到这是一个老问题/线程,但也许这对其他人会有所帮助。
这里有一个通用的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`
}
你只需要使用一些核心技巧就可以实现这一点。例如,如果你知道函数返回什么样的变量(注意EVERYjs函数返回一些东西,默认为undefined
),你可以尝试这样的方法:定义变量
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
),或者它返回的值总是"非false",即未转换为false
语句(如0
、null
等)时,这才有效。
这是我的答案,它从本质上简化了Martin的答案,并基于相同的教程。
承诺的超时包装器:
const timeout = (prom, time) => {
const timeoutError = new Error(`execution time has exceeded the allowed time frame of ${time} ms`);
let timer; // will receive the setTimeout defined from time
timeoutError.name = "TimeoutErr";
return Promise.race([
prom,
new Promise((_r, rej) => timer = setTimeout(rej, time, timeoutError)) // returns the defined timeoutError in case of rejection
]).catch(err => { // handle errors that may occur during the promise race
throw(err);
}) .finally(() => clearTimeout(timer)); // clears timer
}
用于测试目的的承诺:
const fn = async (a) => { // resolves in 500 ms or throw an error if a == true
if (a == true) throw new Error('test error');
await new Promise((res) => setTimeout(res, 500));
return "p2";
}
现在这里有一个测试功能:
async function test() {
let result;
try { // finishes before the timeout
result = await timeout(fn(), 1000); // timeouts in 1000 ms
console.log('• Returned Value :', result, ''n'); // result = p2
} catch(err) {
console.log('• Captured exception 0 : 'n ', err, ''n');
}
try { // don't finish before the timeout
result = await timeout(fn(), 100); // timeouts in 100 ms
console.log(result); // not executed as the timeout error was triggered
} catch (err) {
console.log('• Captured exception 1 : 'n ', err, ''n');
}
try { // an error occured during fn execution time
result = await timeout(fn(true), 100); // fn will throw an error
console.log(result); // not executed as an error occured
} catch (err) {
console.log('• Captured exception 2 : 'n ', err, ''n');
}
}
将产生以下输出:
•返回值:p2•捕获的异常1:TimeoutErr:执行时间已超过允许的100毫秒时间范围在C:''。。。''test promise race''test.js:34在异步测试中(C:''…''test promise race''test.js:63:18)•捕获的异常2:错误:测试错误在fn(C:''…''test promise race''test.js:45:26)测试时(C:''…''test promise race''test.js:72:32)
如果您不想在test
函数中使用try ... catch
指令,您也可以用return
替换超时承诺包装器的catch
部分中的throw
指令。
通过这样做,result
变量将接收否则抛出的错误
然后您可以使用它来检测result
变量是否真的包含错误。
if (result instanceof Error) {
// there was an error during execution
}
else {
// result contains the value returned by fn
}
如果要检查错误是否与定义的超时有关,则必须检查"TimeoutErr"
的error.name
值。
在observing timer
和executing function
之间共享一个变量。
用window.setTimeout
或window.setInterval
实现observing timer
。当observing timer
执行时,它会为共享变量设置出口值。
executing function
不断检查变量值。。如果指定了退出值,则返回。
- 从数组中删除双值,而不仅仅是javascript中的重复值
- javascript 类“chzn-select”不仅仅是一个类名吗?
- 我的Javascript正在切换所有元素,而不仅仅是一个元素
- javascript RegEx 检查字符串中的任何字母(虽然需要忽略其他字形 - 所以不仅仅是 0-9)
- JavaScript:为什么要使用原型来调用函数,而不仅仅是调用函数
- javascript菜单上的活动链接可以处理父链接,而不仅仅是子链接
- 如何实现“;功能超时”;在Javascript中-不仅仅是'setTimeout'
- Javascript从左到右添加动态列表项,然后向下添加——而不仅仅是向下
- 我的基于web的javascript游戏对onkeyup事件不是很敏感.我如何使程序检测所有的击键,而不仅仅是其中的一些
- 包中.json实际上是json中的json,而不仅仅是JavaScript
- 当我检查元素时,我如何使用Javascript设置输入值,而不仅仅是视图,而是元素中的永久值
- .match()包含了所有内容,而不仅仅是属性组(Javascript)
- 使用javascript从网格中获取所有行,而不仅仅是可见页面上的行
- Javascript的小问题-每次都会淡出,而不仅仅是页面加载
- When是"="不仅仅是在Javascript中赋值
- Javascript/Html:我如何改变字体/字体本身的大小,而不仅仅是颜色
- javascript:覆盖(而不仅仅是定义)if语句中的函数
- 可以分配JavaScript原型对象而不仅仅是其属性吗
- 如何在JavaScript中访问粘贴文件的文件名(而不仅仅是数据)
- 在 JavaScript 中等待,而不仅仅是将所有内容包装在 setTimeout 中