javascript - 在 JavaScript 中创建“运行循环”的最佳解决方案
问题描述
所以事实证明,while 循环会阻塞 JavaScript 中的事件循环,如果您希望应用程序响应外部/异步事件,这会阻止它成为创建运行循环的可行解决方案。
我的问题是,使用什么技术使“运行循环”成为无限/恒定但仍然具有高性能并且不会阻塞事件循环。
我进行了快速测试以查看while
、setTimeout(fn, 0)
(浏览器)和setImmediate(fn)
(node.js)的性能。
var x = 1000
var isNode = typeof window == 'undefined'
function a() {
var start = (new Date()).getTime()
var q = 0
function next() {
q++
if (q < x) {
if (isNode) {
setImmediate(next)
} else {
setTimeout(next, 0)
}
} else {
var end = (new Date).getTime()
console.log('a', end - start)
}
}
next()
}
function b() {
var start = (new Date()).getTime()
var q = 0
while (q < x) {
q++
}
var end = (new Date).getTime()
console.log('b', end - start)
}
a()
b()
节点.js:
a = 20
b = 0
浏览器:
a = 5039
b = 0
有没有一种方法可以像在 JavaScript 中执行 while 循环一样高效地执行“无限 while 循环”。setImmediate
比 快得多setTimeout
,但仍然比基本的 while 循环慢很多。
解决方案
您可以使用 async/await 在 while 循环中屈服于事件循环:
// This can only be logged when the event loop
// is yielded to in the infinite loop below.
// So, it can only ever be logged between `#1` and `#2` being logged;
// it can sometimes occur more than once, or not at all between `#1` and `#2`
const interval = setInterval( _ => console.log( '#1.5' ), 0 );
(async _ => {
// Using a for loop so this demo doesn't run forever.
// Can use an infinite loop instead
for ( let i = 0; i < 150; i++ ) { // while( true ) {
console.log( '#1 About to yield, ' );
await new Promise( resolve => setTimeout( _ => resolve(), 0 ) ); // yield
console.log( '#2 Done yielding (other callbacks may have run)' );
}
clearInterval( interval );
})();
在 node.js 中使用setImmediate而不是 setTimeout。不要使用process.nextTick
.
使用您的计时器进行演示:
var start = (new Date()).getTime()
var q = 0, x = 1000;
;(async _ => {
while (q < x) {
await new Promise( resolve => setTimeout( _ => resolve(), 0 ) );
q++
}
var end = (new Date).getTime()
console.log('a', end - start)
})();
推荐阅读
- stack - 堆栈应该增长到低地址,为什么我的代码测试结果是增长到高地址
- regex - 正则表达式捕获特定字符串
- split - 如何在ansible中解码和拆分字符串
- ruby-on-rails - 如果尚未调用,则强制 Webmock stub_request 在测试结束时引发
- ios - 在 macOS Catalina 上运行 iOS 应用程序时,我无法更改 UIWindowScene 中“视图”的位置
- perl - 调用安装在不同路径/目录中的 Perl 模块
- git - 从开发合并到master时如何更改requirements.txt?
- azure - 我们是否有与 GCP 类似的在 Azure 中训练自定义容器的等效程序?
- spring - Spring Boot 启动器缓存工作概念
- node.js - 如何取回推入猫鼬数组中的项目的对象ID