等到 flag=true

IT技术 javascript synchronization
2021-01-16 02:34:03

我有这样的 javascript 函数:

function myFunction(number) {

    var x=number;
    ...
    ... more initializations
    //here need to wait until flag==true
    while(flag==false)
    {}

    ...
    ... do something

}

问题是javascript卡在了while中并卡住了我的程序。所以我的问题是如何在函数中间等待直到标志为真而没有“忙等待”?

6个回答

Javascript 是单线程的,因此页面阻塞行为。您可以使用其他人建议的延迟/Promise方法。最基本的方法是使用window.setTimeout. 例如

function checkFlag() {
    if(flag === false) {
       window.setTimeout(checkFlag, 100); /* this checks the flag every 100 milliseconds*/
    } else {
      /* do something*/
    }
}
checkFlag();

这是一个很好的教程,有进一步的解释:教程

编辑

正如其他人指出的那样,最好的方法是重新构建代码以使用回调。但是,这个答案应该让您了解如何使用window.setTimeout.

虽然一方面我真的很喜欢这个答案,因为它确实是一个 js '等待',如果你想返回一个值,它就变得不太可用了。如果您不返回值,我不太确定该模式是否存在真实世界的用例?
2021-04-03 02:34:03
如果需要,您还可以传递参数:stackoverflow.com/questions/1190642/...
2021-04-06 02:34:03
这是一个很好的答案。在浏览了许多技术论坛后,我几乎要放弃了。我认为它非常适合我,因为我正在返回一个值。它也适用于 Internet Explorer。谢谢。
2021-04-08 02:34:03
您当然可以返回一个Promise并以这种方式实现该功能。但是,除非您使用 ECMA-262,否则这通常需要实现 promise 或 polyfill 的第三方库。在不返回Promise的情况下,最好的方法是使用回调机制向调用者发出结果可用的信号。
2021-04-10 02:34:03
如果出于任何原因我们需要向 checkFlag 函数发送参数,那么我们必须使用匿名/箭头函数,例如, window.setTimeout( () => { checkFlag(params); }, 100);
2021-04-12 02:34:03

因为浏览器中的 javascript 是单线程的(这里不涉及的网络工作者除外)并且一个 javascript 执行线程在另一个线程可以运行之前运行完成,您的语句:

while(flag==false) {}

将永远运行(或直到浏览器抱怨无响应的 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();
    }
}

使用Promise、 async\await 和EventEmitter 的解决方案它允许在没有任何循环的情况下立即对标志更改做出react

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');
}

EventEmitter内置于节点中。在浏览器中,您需要自己包含它,例如使用这个包:https : //www.npmjs.com/package/eventemitter3

可以使用此代码的示例: (1) 起初,lock是错误的。(2) 一些代码调用lockable. 该代码评估if (lock)为假,所以它继续:它设置lock为真,然后继续执行一些逻辑。同时: (3) 其他一些代码调用lockable. 但是,该代码的计算结果if (lock)为 true,因此它会等待 promise,直到发出unlocked事件。(4) 回到第一个调用代码:它完成它的逻辑,设置lock为false,并发出一个unlocked事件。(5) 其他代码现在可以继续执行。
2021-03-19 02:34:03

ES6 与 Async / Await ,

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)
这似乎没有什么帮助,因为 await 的整个基本原理是允许您编写看起来同步的代码。然而,我们在这里遇到了完全相同的问题,即“做某事...”被嵌入到回调中。
2021-03-17 02:34:03
取而代之的是await null,根据您的用例,轻微的睡眠可能是有益的。await new Promise(resolve => setTimeout(resolve, 10))
2021-03-19 02:34:03
@vigdora - await null - 此时函数“返回”,然后在 null 的Promise包装器解析时继续。
2021-03-23 02:34:03
这个真的让我很感兴趣 - 有人能解释一下循环内的非Promise await 如何影响 JS 事件循环并防止应用程序挂起吗?
2021-03-31 02:34:03
人们怎么会错过这个
2021-04-03 02:34:03
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();
    }
}

利用:

waitFor(() => window.waitForMe, () => console.log('got you'))