等到flag = true

时间:2014-03-02 09:22:17

标签: javascript synchronization

我有这样的javascript函数:

function myFunction(number) {

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

    ...
    ... do something

}

问题是javascript一直停留在我的程序中。所以我的问题是如何才能在函数中间等待,直到flag为true而没有“busy-wait”?

20 个答案:

答案 0 :(得分:69)

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

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

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

修改

正如其他人所指出的,最好的方法是重新构造代码以使用回调。但是,这个答案应该让你知道如何模拟'与window.setTimeout的异步行为。

答案 1 :(得分:49)

因为浏览器中的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();
    }
}

答案 2 :(得分:11)

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

答案 3 :(得分:4)

迭代($ .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)
        }
}

答案 4 :(得分:4)

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

答案 5 :(得分:4)

使用 Promise ,async \ await和 EventEmitter 的解决方案,允许对标志更改立即做出反应,根本没有任何循环

const EventEmitter = require('events'); 
const lock = false;
const bus = new EventEmitter();

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

答案 6 :(得分:3)

如果允许在代码上使用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

答案 7 :(得分:2)

具有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)

答案 8 :(得分:1)

有没有人想过这样做?

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

}

答案 9 :(得分:1)

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

刚刚编写了这个库:

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" 

答案 10 :(得分:1)

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

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

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

现在,只要您想等到满足特定条件时,就可以这样称呼它。

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

答案 11 :(得分:1)

我在这里采用了与回调解决方案类似的方法,但是试图使其更加通用。想法是添加一些在队列发生更改后需要执行的功能。当事情发生时,您可以遍历队列,调用函数并清空队列。

向队列添加功能:

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()

这对我很有用,因为在相同条件下我需要等待几件事。它将条件的检测与遇到条件时需要执行的操作分离开来。

答案 12 :(得分:1)

有一个非常易于使用的节点软件包delay

const delay = require('delay');

(async () => {
    bar();

    await delay(100);

    // Executed 100 milliseconds later
    baz();
})();

答案 13 :(得分:1)

我尝试使用@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);
 }

答案 14 :(得分:1)

使用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和arrow函数的一些引用在类似的文章中: https://stackoverflow.com/a/52652681/209794

答案 15 :(得分:0)

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

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

答案 16 :(得分:0)

通过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);

答案 17 :(得分:0)

类似于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
}

答案 18 :(得分:0)

//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(); 
  
  
  
  
  
  

答案 19 :(得分:-1)

受 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;
}