等到flag = true


90

我有这样的javascript函数:

function myFunction(number) {

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

    ...
    ... do something

}

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


3
使用您的初始化的承诺模式-可以在相当长的一段库中找到喜欢的jQuery.DeferredQasync,...
Sirko

确切地在哪里使用以及如何使用?
ilay zeidman 2014年

1
有很多教程描述各种库的promise实现,例如。jQuery.DeferredQ。顺便说一句,您的基本问题与此问题相同。
锡尔科2014年

3
对于在2018年阅读此书的人来说,Properes受Opera mini和IE11之外的所有浏览器支持。
丹尼尔·雷纳

主要问题是,在事件划分的单线程js中无法真正阻止(睡眠)等待。您只能创建等待处理程序。查看更多:stackoverflow.com/questions/41842147/…–
SalientBrain,

Answers:


68

由于浏览器中的javascript是单线程的(此处未涉及的Webworker除外),并且javascript执行的一个线程在另一个线程可以运行之前先运行完毕。

while(flag==false) {}

将永远运行(或直到浏览器抱怨无响应的javascript循环),该页面似乎已被挂起,并且没有其他javascript可以运行,因此该标志的值永远无法更改。

为了进一步说明,Javascript是一种事件驱动语言。这意味着它将运行一段Javascript,直到将控制权返回给解释器。然后,只有当它返回解释器时,Javascript才会从事件队列中获取下一个事件并运行它。

计时器和网络事件之类的所有东西都贯穿事件队列。因此,当计时器触发或网络请求到达时,它永远不会“中断”当前正在运行的Javascript。取而代之的是,将一个事件放入Javascript事件队列中,然后,当当前运行的Javascript完成时,将下一个事件从事件队列中拉出并轮流运行。

因此,当您执行无限循环(例如)时while(flag==false) {},当前正在运行的Javascript永远不会完成,因此下一个事件也不会从事件队列中拉出,因此no的值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();
    }
}

96

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

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

这是一个很好的教程,带有进一步的说明:教程

编辑

正如其他人指出的那样,最好的方法是重新组织代码以使用回调。但是,此答案应该使您了解如何使用来“模拟”异步行为window.setTimeout


1
一方面,我真的很喜欢这个答案,因为它确实是一个js“等待”,如果您想返回一个值,它就变得不太可用。如果不返回值,那么我不确定该模式是否存在真实的用例?
Martin Meeser

您当然可以返回承诺并以这种方式实现功能。但是,这通常需要实现promise或polyfill的第三方库,除非您使用的是ECMA-262。在不返回承诺的情况下,最好的方法是使用回调机制向调用者发送信号,告知结果可用。
基兰

您还可以根据需要传递参数:stackoverflow.com/questions/1190642/…–
SharpC

1
这是一个很好的答案。在浏览了许多技术论坛后,我差点放弃。我认为这非常适合我,因为我正在返回一个值。它也可以在Internet Explorer上工作。非常感谢。
约瑟夫

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

15

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


14

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

1
人们是如何错过这一点的
Aviad

11

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


8

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


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

1

与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
}

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

1

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


1

有一个delay非常容易使用的节点包

const delay = require('delay');

(async () => {
    bar();

    await delay(100);

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

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

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


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


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


0

如果允许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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.