【问题标题】:Wait until flag=true等到 flag=true
【发布时间】:2014-04-03 06:25:19
【问题描述】:

我有这样的javascript函数:

function myFunction(number) {

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

    ...
    ... do something

}

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

【问题讨论】:

  • 使用 promise 模式进行初始化 - 可以在很多库中找到,例如 jQuery.DeferredQasync、...
  • 具体在哪里使用以及如何使用?
  • 有很多关于描述各种库的承诺实现的教程,例如。 jQuery.DeferredQ。顺便说一句,您的根本问题与this question 中的相同。
  • 对于 2018 年阅读这篇文章的人来说,除了 Opera mini 和 IE11 之外,所有浏览器都支持 Promise。
  • 主要问题是在事件驱动的单线程js中不可能做到真正的阻塞(睡眠)等待。您只能创建等待处理程序。查看更多:stackoverflow.com/questions/41842147/…

标签: javascript synchronization


【解决方案1】:

最干净的解决方案(@tdxius 解决方案的改进)基于受控时间间隔循环、承诺和超时来拒绝承诺并在给定时间内不满足条件的情况下清除间隔

const waitUntil = (condition) => {
                    return new Promise((resolve, reject) => {
                        const interval = setInterval(() => {
                            if (!condition()) {
                                return;
                            }

                            clearInterval(interval);
                            resolve();
                        }, 100);

                        setTimeout(() => {
                            clearInterval(interval);
                            reject('your error msg');
                        }, 5000);
                    });
                };

现在,只要你想等到某个条件满足,你就可以这样调用它。

waitUntil(CONDITION_FUNCTION)
  .then(() => DO_SOMETHING)
  .catch((YOUR_ERROR_MSG) => console.warn(YOUR_ERROR_MSG))

【讨论】:

    【解决方案2】:

    类似于 Lightbeard 的回答,我使用以下方法

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms))
    }
    
    async function until(fn) {
        while (!fn()) {
            await sleep(0)
        }
    }
    
    async function myFunction(number) {
        let x = number
        ...
        ... more initialization
        
        await until(() => flag == true)
    
        ...
        ... do something
    }
    

    【讨论】:

      【解决方案3】:

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

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

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

      编辑

      正如其他人所指出的,最好的方法是重新构建代码以使用回调。但是,这个答案应该让您了解如何使用window.setTimeout“模拟”异步行为。

      【讨论】:

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

      有没有人想过这样做?

      function resolveAfter2Seconds() {
        return new Promise(resolve => {
          setTimeout(() => {
            resolve();
          }, 2000);
        });
      }
      
      
      
      
      function myFunction(number) {
      
          var x=number;
          ...
          ... more initializations
          //here need to wait until flag==true
          while(flag==false)
          {
               await resolveAfter2Seconds();
          }
      
          ...
          ... do something
      
      }
      

      【讨论】:

        【解决方案5】:

        尽量避免while循环,因为它可能会阻塞你的代码,使用异步和promise。

        刚刚写了这个库:

        https://www.npmjs.com/package/utilzed

        有个函数waitForTrue

        import utilzed from 'utilzed'
        
        const checkCondition = async () => {
          // anything that you are polling for to be expecting to be true
          const response = await callSomeExternalApi();
          return response.success;
        }
        
        // this will waitForTrue checkCondition to be true
        // checkCondition will be called every 100ms
        const success = await utilzed.waitForTrue(100, checkCondition, 1000);
        
        if (success) {
          // Meaning checkCondition function returns true before 1000 ms
          return;
        }
        
        // meaning after 1000ms the checkCondition returns false still
        // handle unsuccessful "poll for true" 
        

        【讨论】:

          【解决方案6】:

          受 jfriend00 的启发,这对我有用

          const seconds = new Date();
          // wait 5 seconds for flag to become true
          const waitTime = 5
          const extraSeconds = seconds.setSeconds(seconds.getSeconds() + waitTime);
          while (Date.now() < extraSeconds) {
            // break when flag is false
            if (flag === false) break;
          }

          【讨论】:

          • 这只会检查一次?
          • 循环运行 5 秒并始终检查标志是否仍然为真或假
          【解决方案7】:

          我通过实现下面的方法解决了这个问题。

          const waitUntil = (condition) => {
              return new Promise((resolve) => {
                  let interval = setInterval(() => {
                      if (!condition()) {
                          return
                      }
          
                      clearInterval(interval)
                      resolve()
                  }, 100)
              })
          }
          

          现在,只要你想等到满足某个条件,你就可以这样调用它。

          await waitUntil(() => /* your condition */)
          

          【讨论】:

          • 我认为调用中应该是等待而不是等待
          • @mega_creamery 感谢您的更正。我修正了错字:)
          • 在我看来这是最干净、最简单的解决方案
          【解决方案8】:

          我采取了一种与此处的回调解决方案类似的方法,但试图使其更通用一点。这个想法是您添加在队列发生更改后需要执行的函数。当事情发生时,你会遍历队列,调用函数并清空队列。

          将函数添加到队列中:

          let _queue = [];
          
          const _addToQueue = (funcToQ) => {
              _queue.push(funcToQ);
          }
          

          执行并刷新队列:

          const _runQueue = () => {
              if (!_queue || !_queue.length) {
                  return;
              }
          
              _queue.forEach(queuedFunc => {
                  queuedFunc();
              });
          
              _queue = [];
          }
          

          当您调用 _addToQueue 时,您需要包装回调:

          _addToQueue(() => methodYouWantToCallLater(<pass any args here like you normally would>));
          

          满足条件后,拨打_runQueue()

          这对我很有用,因为我有几件事需要在相同的条件下等待。并且它将条件检测与满足条件时需要执行的任何操作分离。

          【讨论】:

            【解决方案9】:

            如果你被允许在你的代码上使用:async/await,你可以试试这个:

            const waitFor = async (condFunc: () => boolean) => {
              return new Promise((resolve) => {
                if (condFunc()) {
                  resolve();
                }
                else {
                  setTimeout(async () => {
                    await waitFor(condFunc);
                    resolve();
                  }, 100);
                }
              });
            };
            
            const myFunc = async () => {
              await waitFor(() => (window as any).goahead === true);
              console.log('hello world');
            };
            
            myFunc();
            

            这里的演示: https://stackblitz.com/edit/typescript-bgtnhj?file=index.ts

            在控制台上,只需复制/粘贴:goahead = true

            【讨论】:

              【解决方案10】:

              有一个node包delay非常好用

              const delay = require('delay');
              
              (async () => {
                  bar();
              
                  await delay(100);
              
                  // Executed 100 milliseconds later
                  baz();
              })();
              

              【讨论】:

                【解决方案11】:

                在我的示例中,我每秒记录一个新的计数器值:

                var promises_arr = [];
                var new_cntr_val = 0;
                
                // fill array with promises
                for (let seconds = 1; seconds < 10; seconds++) {
                    new_cntr_val = new_cntr_val + 5;    // count to 50
                    promises_arr.push(new Promise(function (resolve, reject) {
                        // create two timeouts: one to work and one to resolve the promise
                        setTimeout(function(cntr) {
                            console.log(cntr);
                        }, seconds * 1000, new_cntr_val);    // feed setTimeout the counter parameter
                        setTimeout(resolve, seconds * 1000);
                    }));
                }
                
                // wait for promises to finish
                Promise.all(promises_arr).then(function (values) {
                    console.log("all promises have returned");
                });

                【讨论】:

                  【解决方案12】:

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

                  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) 其他代码现在可以继续执行。
                  【解决方案13】:

                  通过EventTarget API使用非阻塞javascript

                  在我的示例中,我需要等待回调才能使用它。我不知道何时设置此回调。它可以在我需要执行它之前或之后。而且我可能需要多次调用它(一切都是异步的)

                  // bus to pass event
                  const bus = new EventTarget();
                  
                  // it's magic
                  const waitForCallback = new Promise((resolve, reject) => {
                      bus.addEventListener("initialized", (event) => {
                          resolve(event.detail);
                      });
                  });
                  
                  
                  
                  // LET'S TEST IT !
                  
                  
                  // launch before callback has been set
                  waitForCallback.then((callback) => {
                      console.log(callback("world"));
                  });
                  
                  
                  // async init
                  setTimeout(() => {
                      const callback = (param) => { return `hello ${param.toString()}`; }
                      bus.dispatchEvent(new CustomEvent("initialized", {detail: callback}));
                  }, 500);
                  
                  
                  // launch after callback has been set
                  setTimeout(() => {
                      waitForCallback.then((callback) => {
                          console.log(callback("my little pony"));
                      });
                  }, 1000);

                  【讨论】:

                    【解决方案14】:

                    我尝试使用@Kiran 方法,如下所示:

                    checkFlag: function() {
                      var currentObject = this; 
                      if(flag == false) {
                          setTimeout(currentObject.checkFlag, 100); 
                       } else {
                         /* do something*/
                       }
                    }
                    

                    (我使用的框架迫使我以这种方式定义函数)。 但没有成功,因为当第二次执行进入 checkFlag 函数时,this 不是我的对象,它是Window。 所以,我完成了下面的代码

                    checkFlag: function() {
                        var worker = setInterval (function(){
                             if(flag == true){             
                                 /* do something*/
                                  clearInterval (worker);
                             } 
                        },100);
                     }
                    

                    【讨论】:

                      【解决方案15】:

                      使用 Promise 的现代解决方案

                      原题中的myFunction()可以修改如下

                      async function myFunction(number) {
                      
                          var x=number;
                          ...
                          ... more initializations
                      
                          await until(_ => flag == true);
                      
                          ...
                          ... do something
                      
                      }
                      

                      until() 是这个实用函数

                      function until(conditionFunction) {
                      
                        const poll = resolve => {
                          if(conditionFunction()) resolve();
                          else setTimeout(_ => poll(resolve), 400);
                        }
                      
                        return new Promise(poll);
                      }
                      

                      在类似的帖子中对 async/await 和箭头函数的一些引用: https://stackoverflow.com/a/52652681/209794

                      【讨论】:

                      • 这是我见过的唯一解决方案,它可以轻松地让您“等待”信号,但在满足条件之前不能从 myFunction“返回”。
                      • 我已经尝试了最重要的几个答案,这个答案是迄今为止效果最好的。它不仅是最简单和最干净的,而且是最符合逻辑和语法意义的。谢谢!
                      • 我更喜欢条件运算符:conditionFunction() ? resolve() : setTimeout(() => poll(resolve), 400)
                      【解决方案16】:

                      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)
                      

                      【讨论】:

                      • 人们怎么会错过这个
                      • 这个真的让我很感兴趣 - 谁能解释一下循环中未承诺的等待如何影响 JS 事件循环并防止应用挂起?
                      • @vigdora - await null - 此时函数“返回”,然后在 null 的 promise 包装器解析时继续。
                      • 这似乎没什么帮助,因为 await 的全部原理是允许您编写看起来同步的代码。然而,在这里我们遇到了完全相同的问题,即“做某事......”被嵌入到回调中。
                      • 而不是await null,根据您的用例,稍微睡眠可能会有所帮助。 await new Promise(resolve =&gt; setTimeout(resolve, 10))
                      【解决方案17】:

                      //function a(callback){
                      setTimeout(function() {
                        console.log('Hi I am order 1');
                      }, 3000);
                       // callback();
                      //}
                      
                      //function b(callback){
                      setTimeout(function() {
                        console.log('Hi I am order 2');
                      }, 2000);
                      //   callback();
                      //}
                      
                      
                      
                      //function c(callback){
                      setTimeout(function() {
                        console.log('Hi I am order 3');
                      }, 1000);
                      //   callback();
                      
                      //}
                      
                       
                      /*function d(callback){
                        a(function(){
                          b(function(){
                            
                            c(callback);
                            
                          });
                          
                        });
                        
                        
                      }
                      d();*/
                      
                      
                      async function funa(){
                        
                        var pr1=new Promise((res,rej)=>{
                          
                         setTimeout(()=>res("Hi4 I am order 1"),3000)
                              
                        })
                        
                        
                         var pr2=new Promise((res,rej)=>{
                          
                         setTimeout(()=>res("Hi4 I am order 2"),2000)
                              
                        })
                         
                          var pr3=new Promise((res,rej)=>{
                          
                         setTimeout(()=>res("Hi4 I am order 3"),1000)
                              
                        })
                      
                                    
                        var res1 = await pr1;
                        var res2 = await pr2;
                        var res3 = await pr3;
                        console.log(res1,res2,res3);
                        console.log(res1);
                         console.log(res2);
                         console.log(res3);
                      
                      }   
                          funa();
                                    
                      
                      
                      async function f1(){
                        
                        await new Promise(r=>setTimeout(r,3000))
                          .then(()=>console.log('Hi3 I am order 1'))
                          return 1;                        
                      
                      }
                      
                      async function f2(){
                        
                        await new Promise(r=>setTimeout(r,2000))
                          .then(()=>console.log('Hi3 I am order 2'))
                               return 2;                   
                      
                      }
                      
                      async function f3(){
                        
                        await new Promise(r=>setTimeout(r,1000))
                          .then(()=>console.log('Hi3 I am order 3'))
                              return 3;                    
                      
                      }
                      
                      async function finaloutput2(arr){
                        
                        return await Promise.all([f3(),f2(),f1()]);
                      }
                      
                      //f1().then(f2().then(f3()));
                      //f3().then(f2().then(f1()));
                        
                      //finaloutput2();
                      
                      //var pr1=new Promise(f3)
                      
                      
                      
                      
                      
                      
                      
                      async function f(){
                        console.log("makesure");
                        var pr=new Promise((res,rej)=>{
                        setTimeout(function() {
                        console.log('Hi2 I am order 1');
                      }, 3000);
                        });
                          
                        
                        var result=await pr;
                        console.log(result);
                      }
                      
                       // f(); 
                      
                      async function g(){
                        console.log("makesure");
                        var pr=new Promise((res,rej)=>{
                        setTimeout(function() {
                        console.log('Hi2 I am order 2');
                      }, 2000);
                        });
                          
                        
                        var result=await pr;
                        console.log(result);
                      }
                        
                      // g(); 
                      
                      async function h(){
                        console.log("makesure");
                        var pr=new Promise((res,rej)=>{
                        setTimeout(function() {
                        console.log('Hi2 I am order 3');
                      }, 1000);
                        });
                          
                        
                        var result=await pr;
                        console.log(result);
                      }
                      
                      async function finaloutput(arr){
                        
                        return await Promise.all([f(),g(),h()]);
                      }
                        
                      //finaloutput();
                      
                       //h(); 
                        
                        
                        
                        
                        
                        

                      【讨论】:

                        【解决方案18】:

                        使用 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
                        };

                        【讨论】:

                        • 延迟是否需要异步?
                        【解决方案19】:

                        因为浏览器中的 javascript 是单线程的(此处未涉及的 webworkers 除外),并且一个 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();
                            }
                        }
                        

                        【讨论】:

                          【解决方案20】:
                          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'))
                          

                          【讨论】:

                            【解决方案21】:

                            用于迭代 ($.each) 对象并在每个对象上执行长时间运行的操作(包含嵌套的 ajax 同步调用):

                            我首先在每个上设置一个自定义 done=false 属性。

                            然后,在递归函数中,设置每个done=true 并继续使用setTimeout。 (这是一个意味着停止所有其他 UI、显示进度条并阻止所有其他使用的操作,因此我原谅了自己的同步调用。)

                            function start()
                            {
                                GlobalProducts = getproductsfromsomewhere();
                                $.each(GlobalProducts, function(index, product) {
                                     product["done"] = false;
                                });
                            
                                DoProducts();
                            }
                            function DoProducts()
                            {
                                var doneProducts = Enumerable.From(GlobalProducts).Where("$.done == true").ToArray(); //linqjs
                            
                                //update progress bar here
                            
                                var nextProduct = Enumerable.From(GlobalProducts).Where("$.done == false").First();
                            
                                    if (nextProduct) {
                                        nextProduct.done = true;
                                        Me.UploadProduct(nextProduct.id); //does the long-running work
                            
                                        setTimeout(Me.UpdateProducts, 500)
                                    }
                            }
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2019-11-11
                              • 1970-01-01
                              • 2019-06-10
                              • 1970-01-01
                              • 1970-01-01
                              相关资源
                              最近更新 更多