[英]Is there a reason using setTimeout within an on-data event from a child process causes arbitrarily long delays?
我遇到过在 NW.js 中使用 Javascript 的场景,因此setTimeout()
会遇到任意长的延迟,而不是在请求的持续时间之后执行回调。 一般在任何地方都有几十毫秒到几千毫秒的意外延迟。
当然, setTimeout()
并不能保证它会在请求时准确地执行回调,但它通常非常非常接近。我的期望是情况总是如此,只要主线程没有过载其他任务。 例如,如果我混搭 F12 并打开浏览器控制台并运行以下命令:
var ts = Date.now();
setTimeout(function () {
console.log(`This should run immediately! It took ${Date.now() - ts}ms`);
}, 0);
我希望输出如下:
This should run immediately! It took 1ms
This should run immediately! It took 1ms
(可能是 0-5 毫秒,视情况而定——但绝对是很少的时间,而且如果你一次又一次地运行它,它是可重复的)
但是,在上述情况下,延迟可能很大; 通常比请求的持续时间长数千毫秒。
它似乎只需要在来自子进程的 onData 事件之后使用setTimeout()
。 它可以直接在process.stdout.on('data')
回调中使用,也可以在稍后的执行链中使用。 在这两种情况下, setTimeout()
似乎行为不端。
没有其他行为不端; Javascript 继续正常执行,Promises 及时解决,等等......只要不涉及setTimeout()
。 即使像使用 For 循环来强加延迟而不是setTimeout()
这样可怕的事情也能可靠地工作。
我准备了一个例子来复制我在我的系统上观察到的东西,希望其他人可以测试它,看看我是否做错了什么!
我希望有人会转过身来告诉我我忽略了一些东西!
请参阅下面的代码。 我已经在多台机器上尝试过,观察到相同的行为。 请注意给出的测试用例是我迄今为止发现的最低限度的例子,它复制了这个问题。 它并不代表我在实践中实际尝试做的事情; 相反,它只是试图复制我在我的项目中遇到的行为不端的setTimeout()
问题(据我所知,它确实复制了它)。
这个场景需要:
下载并解压相关的 NW.js SDK,然后在文件夹中插入以下文件并运行nw.exe
。 您应该会看到一个白色窗口出现; 打开 devtools (F12) 并使用td.beginBasicTest()
执行测试:
包.json:
{
"name": "STT",
"description": "setTimeout Test",
"main": "test.html",
"node-remote": "http://127.0.0.1",
"window": {
"title": "test",
"show": true,
"toolbar": true,
"fullscreen": false,
"width": 500,
"height": 500,
"frame": false,
"position": "center",
"resizeable": true
},
"dependencies": {
},
"devDependencies": {},
"chromium-args": "--password-store=basic --disable-pinch --disable-background-timer-throttling --disable-raf-throttling"
}
child_process.py:
#!/usr/bin/env python3
import sys
import asyncio
import json
class TestProcess:
def __init__(self):
self.alive = True
self.run = True
def __hello(self):
hello_msg = '{"resp": "hello"}'
print(hello_msg, flush=True)
async def __stdin_msg_handler(self, inp):
# read the input command and respond accordingly
if(len(inp) > 0):
try:
# convert from JSON to dict
msg = json.loads(inp[0])
except:
# couldn't decode message; ignore it?
print('{"resp":"badCommand"}', flush=True)
msg = None
if(msg):
if(msg["cmd"] == "hello"):
self.__hello()
async def __listen(self):
line = await self.loop.run_in_executor(None, sys.stdin.readline)
if(len(line) > 0):
msgs = line.split("\r")
return msgs
else:
return False
async def __loop(self):
while self.run == True:
msgs = await self.__listen()
if(msgs):
await self.__stdin_msg_handler(msgs)
def start(self):
self.loop = asyncio.get_event_loop()
self.loop.run_until_complete(self.__loop())
if __name__ == '__main__':
tproc = TestProcess()
tproc.start()
测试.html:
<head>
<script>
const spawn = require(`child_process`).spawn; // ability to execute background tasks/commands
class TestDelay {
constructor (delay = 5) {
this.childProcessPath = `child_process.py`;
this._startProcess();
}
beginBasicTest (delay = this.defaultDelay) {
this._pTestProcedureWithComms(delay)
.then(() => {
console.log(`Test completed - did it work? Try it a few times, performance seems to vary...`);
});
}
_startProcess () {
this.childProcess = spawn(`py`, [`-3`, this.childProcessPath], { stdio: `pipe` });
this.childRunning = true;
this.childProcess.stdout.on(`data`, (d) => {
console.log(`stdout: ${d}`);
var ts = Date.now();
setTimeout(function () {
console.log(`This should run immediately! It took ${Date.now() - ts}ms`);
}, 0);
setTimeout(function () {
console.log(`This should run after 5s! It took ${Date.now() - ts}ms`);
}, 5000);
});
this.childProcess.stderr.on(`data`, (d) => {
console.warn(`stderr: ${d}`);
});
this.childProcess.on(`exit`, (code) => {
this.childRunning = false;
console.log(`child process exited with code ${code}`);
});
this.childProcess.on(`close`, (code) => {
console.log(`child process IO closed with code ${code}`);
});
console.log(`Started child process...`);
}
_pTestProcedureWithComms (delay = this.defaultDelay) {
return new Promise(resolve => {
this._pSendToProcess({ cmd: `hello` })
.then(resolve);
});
}
_pSendToProcess (text = ``) {
return new Promise(resolve => {
this.childProcess.stdin.write(JSON.stringify(text), `utf-8`);
this.childProcess.stdin.write(`\n`, `utf-8`);
resolve();
});
}
}
var td = new TestDelay();
</script>
</head>
<body>
Hello!
</body>
当我多次运行测试时,我经常观察到 setTimeout 似乎行为不端。 这是我在多次运行测试时得到的一些示例输出:
有时您的立即/5 秒报告为1ms/5010ms
,有时报告为8714ms/8715ms
告诉我,这是一个问题,您的事件循环已满并在setTimeout
可以运行之前等待其他事情完成。
我对 stdio/stdout/stderr 的了解还不够多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.