[英]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.