如何进行简单的非阻塞Javascript函数调用?例如:
//begin the program
console.log('begin');
nonBlockingIncrement(10000000);
console.log('do more stuff');
//define the slow function; this would normally be a server call
function nonBlockingIncrement(n){
var i=0;
while(i<n){
i++;
}
console.log('0 incremented to '+i);
}
产出
"beginPage"
"0 incremented to 10000000"
"do more stuff"
我如何构造这个简单的循环来异步执行并通过回调函数输出结果呢?这个想法是为了不阻塞“做更多的事情”:
"beginPage"
"do more stuff"
"0 incremented to 10000000"
我尝试过学习回调和延续的教程,但它们似乎都依赖于外部库或函数,没有一个能在真空中回答这个问题:如何编写非阻塞的Javascript代码?
我在问这个问题之前,曾非常努力地寻找这个答案;请不要以为我没看过我找到的所有东西都是Node.js特有的(1(https://stackoverflow.com/questions/2878008/how-do-i-create-a-non-blocking-asynchronous-function-in-node-js)、2(https://stackoverflow.com/questions/23757363/how-to-create-non-blocking-asynchronous-function-in-node-js-and-express-js)、3(https://stackoverflow.com/questions/9362823/why-is-a-function-and-a-callback-non-blocking-in-node-js)、4(https://stackoverflow.com/questions/5670190/how-do-i-write-non-blocking-code-in-node-js)、5(https://stackoverflow.com/questions/9546225/nodejs-how-to-create-a-non-blocking-computation))或其他特定于其他函数或库的函数或库([6],7(https://stackoverflow.com/questions/7729382/how-to-make-a-non-blocking-sleep-in-javascript-jquery),8(https://stackoverflow.com/questions/6880392/are-jquery-fadein-animation-functions-non-blocking),9(https://stackoverflow.com/questions/15855149/non-blocking-script-not-working-with-jquery),10(https://stackoverflow.com/questions/3845972/whats-the-best-way-to-implement-a-sleep-function-javascript),11(https://stackoverflow.com/questions/10180391/javascript-how-to-avoid-blocking-the-browser-while-doing-heavy-work)),特别是JQuery和setTimeout()
。请帮助我使用 Javascript 编写非阻塞代码,而不是JQuery和Node等Javascript编写的工具。请在标记为重复之前重新阅读问题。
8条答案
按热度按时间polkgigr1#
要使循环不阻塞,必须将其分成几个部分,并允许JS事件处理循环在进入下一部分之前使用用户事件。
实现这一点最简单的方法是完成一定量的工作,然后使用
setTimeout(..., 0)
对下一个工作块进行排队,最重要的是,这种排队允许JS事件循环在进入下一个工作块之前处理在此期间排队的任何事件:用法:
请参见http://jsfiddle.net/alnitak/x3bwjjo6/的演示,其中
callback
函数只是将变量设置为当前迭代计数,一个单独的基于setTimeout
的循环轮询该变量的当前值,并用其值更新页面。q1qsirdb2#
带回调的SetTimeout是一种可行的方法。但是,要知道你的函数作用域与C#或其他多线程环境中的不同。
Javascript不会等待函数的回调完成。
如果你说:
您的警报将在您传递的函数之前触发。
不同之处在于警报阻塞了线程,但回调没有阻塞。
bjg7j2ky3#
据我所知,通常有两种方法可以做到这一点。一种是使用
setTimeout
(或者requestAnimationFrame
,如果你是在一个支持环境中这样做的话)。@Alnitak在另一个答案中展示了如何做到这一点。另一种方法是使用一个web工作者在一个单独的线程中完成你的阻塞逻辑,这样主UI线程就不会被阻塞。使用
requestAnimationFrame
或setTimeout
:使用Web辅助进程:
无法运行Web辅助进程解决方案,因为它需要单独的
worker.js
文件来承载辅助进程逻辑。41zrol4v4#
你不能同时执行两个循环,记住JS是单线程的.
"所以,这样做是行不通的"
如果你想实现多线程,你必须使用Web Workers,但是他们必须有一个单独的js文件,你只能向他们传递对象。
但是,我已经通过生成Blob文件成功地使用了Web Workers,没有分隔文件,我也可以向它们传递回调函数。
ars1skjm5#
对于非常长的任务,Web-Worker应该是首选,但是对于足够小的任务(〈几秒)或者当您无法将任务移动到Worker时(例如,因为您需要访问DOM或诸如此类的东西,Alnitak的将代码拆分为块的解决方案是可行的方法)。
现在,由于
async/await
语法,它可以用更干净的方式重写。此外,与其等待
setTimeout()
(在node-js中至少延迟1ms,在第5次递归调用之后,在任何地方都延迟4ms),最好使用MessageChannel。所以这给了我们
laik7k3q6#
使用ECMA异步函数,即使它执行CPU绑定的操作,也很容易编写非阻塞异步代码。让我们在一个典型的学术任务上做这个-,计算令人难以置信的巨大值的Fibonacci。您所需要的只是插入一个操作,允许不时地到达事件循环。使用这种方法,您永远不会冻结用户界面或I/O。
基本实施:
现在我们可以使用它(Live Demo):
正如我们所看到的,计时器运行正常,这表明事件循环没有阻塞。
我以antifreeze2 npm包的形式发布了这些助手的改进实现,它在内部使用
setImmediate
,因此要获得最大性能,您需要为没有本机支持的环境导入setImmediate polyfill。Live Demo
368yc8dk7#
如果您使用的是jQuery,我创建了Alnitak's answer的延迟实现
然后,您就可以使用返回的promise来管理进度和done回调:
brgchamk8#
我设法用函数得到了一个非常短的算法。下面是一个例子:
我知道这看起来很复杂,所以让我解释一下到底是怎么回事。
下面是l函数的一个稍微简化的版本。
循环的第一步,l_smpl调用回调函数并传入d -索引。如果d未定义,就像第一次调用时一样,它会将其更改为0。
接下来,它通过调用updater函数更新d,并将d设置为结果,在我们的例子中,updater函数会将索引加1。
下一步检查条件是否满足,方法是调用第一个函数,并检查值是否为true,即循环未完成。如果是,则再次调用函数,否则返回0以结束循环。