ila*_*man 68 javascript synchronization
我有这样的javascript函数:
function myFunction(number) {
var x=number;
...
... more initializations
//here need to wait until flag==true
while(flag==false)
{}
...
... do something
}
Run Code Online (Sandbox Code Playgroud)
问题是javascript被困在了一段时间并且卡住了我的程序.所以我的问题是如何才能在函数中间等待,直到flag为true而没有"busy-wait"?
Kir*_*ran 83
Javascript是单线程的,因此页面阻塞行为.您可以使用其他人建议的延迟/承诺方法,但最基本的方法是使用window.setTimeout.例如
function checkFlag() {
if(flag == false) {
window.setTimeout(checkFlag, 100); /* this checks the flag every 100 milliseconds*/
} else {
/* do something*/
}
}
checkFlag();
Run Code Online (Sandbox Code Playgroud)
这是一个很好的教程,有进一步的解释:教程
编辑
正如其他人所指出的,最好的方法是重新构造代码以使用回调.但是,这个答案应该让您了解如何使用"模拟"异步行为window.setTimeout.
jfr*_*d00 58
因为浏览器中的javascript是单线程的(除了这里没有涉及的webworkers)并且javascript执行的一个线程在另一个可以运行之前运行完成,你的语句:
while(flag==false) {}
Run Code Online (Sandbox Code Playgroud)
将永远运行(或直到浏览器抱怨无响应的javascript循环),页面将显示为挂起,没有其他javascript将有机会运行,因此标志的值永远不会被更改.
对于更多解释,Javascript是一种事件驱动的语言.这意味着它运行一段Javascript,直到它将控制权返回给解释器.然后,只有当它返回到解释器时,Javascript才从事件队列中获取下一个事件并运行它.
计时器和网络事件等所有内容都通过事件队列运行.因此,当计时器触发或网络请求到达时,它不会"中断"当前运行的Javascript.相反,一个事件被放入Javascript事件队列中,然后,当当前运行的Javascript完成时,下一个事件将从事件队列中拉出并轮到它运行.
因此,当您执行无限循环时while(flag==false) {},当前运行的Javascript永远不会完成,因此下一个事件永远不会从事件队列中拉出,因此flag永远不会更改值.它们的关键在于Javascript不是中断驱动的.当计时器触发时,它不会中断当前运行的Javascript,运行其他一些Javascript,然后让当前运行的Javascript继续.它只是放入事件队列中等待,直到当前运行的Javascript完成以轮到它运行.
您需要做的是重新思考代码的工作方式,并找到一种不同的方法来触发flag值在更改时要运行的代码.Javascript被设计为事件驱动的语言.所以,你需要做的是弄清楚你可以注册哪些事件感兴趣,这样你就可以监听可能导致标志改变的事件,你可以检查该事件的标志,或者你可以从任何代码都可能改变标志,或者你可以实现一个回调函数,无论代码改变哪个代码都可以调用你的回调,只要负责更改标志值的代码将改变它的值true,它只调用回调函数,从而调用你的代码想要在标志设置true为时运行将在正确的时间运行.这比尝试使用某种计时器不断检查标志值要高效得多.
function codeThatMightChangeFlag(callback) {
// do a bunch of stuff
if (condition happens to change flag value) {
// call the callback to notify other code
callback();
}
}
Run Code Online (Sandbox Code Playgroud)
Lig*_*ard 14
使用 Promise 的现代解决方案
myFunction() 在原始问题中可以修改如下
async function myFunction(number) {
var x=number;
...
... more initializations
await until(_ => flag == true);
...
... do something
}
Run Code Online (Sandbox Code Playgroud)
until()这个效用函数在哪里
function until(conditionFunction) {
const poll = resolve => {
if(conditionFunction()) resolve();
else setTimeout(_ => poll(resolve), 400);
}
return new Promise(poll);
}
Run Code Online (Sandbox Code Playgroud)
一些对 async/await 和箭头函数的引用在类似的帖子中:https : //stackoverflow.com/a/52652681/209794
小智 13
function waitFor(condition, callback) {
if(!condition()) {
console.log('waiting');
window.setTimeout(waitFor.bind(null, condition, callback), 100); /* this checks the flag every 100 milliseconds*/
} else {
console.log('done');
callback();
}
}
Run Code Online (Sandbox Code Playgroud)
使用:
waitFor(() => window.waitForMe, () => console.log('got you'))
Run Code Online (Sandbox Code Playgroud)
就调用代码的可读性和实现代码的简洁性而言最简单:
const until = (predFn) => {
const poll = (done) => (predFn() ? done() : setTimeout(() => poll(done), 500));
return new Promise(poll);
};
Run Code Online (Sandbox Code Playgroud)
调用代码示例:
await until(() => { myBankBalance > 1000000 });
Run Code Online (Sandbox Code Playgroud)
更详细的例子:
https://replit.com/@SteveChambers1/Javascript-until-function?v=1
我通过实施以下方法解决了这个问题。
const waitUntil = (condition) => {
return new Promise((resolve) => {
let interval = setInterval(() => {
if (!condition()) {
return
}
clearInterval(interval)
resolve()
}, 100)
})
}
Run Code Online (Sandbox Code Playgroud)
现在,每当您想等到满足某个条件时,都可以这样调用。
await waitUntil(() => /* your condition */)
Run Code Online (Sandbox Code Playgroud)
具有Async / Await的ES6
let meaningOfLife = false;
async function waitForMeaningOfLife(){
while (true){
if (meaningOfLife) { console.log(42); return };
await null; // prevents app from hanging
}
}
waitForMeaningOfLife();
setTimeout(()=>meaningOfLife=true,420)
Run Code Online (Sandbox Code Playgroud)
使用Promise,async \ await和EventEmitter的解决方案允许对标志更改立即做出反应,而无需任何循环
const EventEmitter = require('events');
const bus = new EventEmitter();
let lock = false;
async function lockable() {
if (lock) await new Promise(resolve => bus.once('unlocked', resolve));
....
lock = true;
...some logic....
lock = false;
bus.emit('unlocked');
}
Run Code Online (Sandbox Code Playgroud)
EventEmitter内置在节点中。在浏览器中,您需要自己添加它,例如使用以下程序包:https : //www.npmjs.com/package/eventemitter3
使用Ecma Script 2017,您可以使用async-await和while一起执行此操作,而不会崩溃或锁定程序,即使变量永远不会为真
//First define some delay function which is called from async function
function __delay__(timer) {
return new Promise(resolve => {
timer = timer || 2000;
setTimeout(function () {
resolve();
}, timer);
});
};
//Then Declare Some Variable Global or In Scope
//Depends on you
var flag = false;
//And define what ever you want with async fuction
async function some() {
while (!flag)
await __delay__(1000);
//...code here because when Variable = true this function will
};Run Code Online (Sandbox Code Playgroud)