如何在 JavaScript 中等待谓词条件变为 true?

我有这样的 javascript 函数:

function myFunction(number) {


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


...
... do something


}

问题是 javascript 卡住了,并且卡住了我的程序。所以我的问题是如何在函数中间等待,直到标志为 true 而不用“忙等待”?

298971 次浏览

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“模拟”异步行为。

因为浏览器中的 javascript 是单线程的(除了这里没有涉及到的 webworker) ,并且一个线程的 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();
}
}

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

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

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

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

使用 EcmaScript2017,您可以使用异步等待和 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
};

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

  

  

  

  

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)

使用承诺的现代解决方案

原问题中的 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);
}

在一篇类似的文章中提到了异步/等待和箭头函数: Https://stackoverflow.com/a/52652681/209794

解决方案使用 我保证,异步等待和 事件发射器允许立即反应的标志改变没有任何类型的循环在所有

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

与光胡子的回答类似,我使用以下方法

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
}

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

事件目标 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);

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

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

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

const delay = require('delay');


(async () => {
bar();


await delay(100);


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

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

我在这里采用了类似于回调解决方案的方法,但是试图使它更通用一些。其思想是添加在队列发生变化后需要执行的函数。当这种情况发生时,然后循环遍历队列,调用函数并清空队列。

向队列添加函数:

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

这对我来说很有用,因为在相同的条件下我有几件事情需要等待。并且它将条件的检测与该条件被触发时需要执行的任何操作解耦。

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

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

现在,无论何时你想等待,直到某个条件得到满足,你可以这样称呼它。

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

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


async function start() {
let flag = false;
  

console.log('wait 5 sec');
  

setTimeout(()=> {flag=true}, 5000); // set flag=true after 5 seconds
  

await waitUntil(() => flag==true ); // wait
  

console.log('do something when flag is true...');
}


start();

受 jfriend 00的启发,我成功了

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

尽量避免 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"

有人想过这么做吗?

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


}

在给定时间内,基于受控时间间隔循环、承诺和超时拒绝承诺和清除间隔的最干净解(@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))

(= “太多的代码... ... 没有阅读”)

就调用代码的可读性和实现代码的简洁性而言,是最简单的:

const until = (predFn) => {
const poll = (done) => (predFn() ? done() : setTimeout(() => poll(done), 500));
return new Promise(poll);
};

示例调用代码:

await until(() => { myBankBalance > 1000000 });

更详细的例子:

Https://replit.com/@stevechambers1/javascript-until-function?v=1