我有这样的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”?
答案 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)
在我的示例中,我需要等待回调才能使用它。我不知道何时设置此回调。它可以在我需要执行它之前。而且我需要多次调用它(一切异步)
// 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;
}