Подтвердить что ты не робот

Подождите, пока флаг = истина

У меня есть функция javascript, например:

function myFunction(number) {

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

    ...
    ... do something

}

Проблема в том, что javascript застрял в то время и застрял в моей программе. поэтому мой вопрос: как я могу ждать в середине функции до тех пор, пока флаг не будет истинным без "ожидание-ожидание"?

4b9b3361

Ответ 1

Поскольку javascript в браузере является однопоточным (за исключением веб-работников, которые здесь не участвуют), и один поток выполнения 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

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.

Ответ 3

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

Ответ 4

Решение с использованием 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

Ответ 5

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)

Ответ 6

С Ecma Script 2017 Вы можете использовать async-await и в то же время вместе, чтобы сделать это, и при этом не будет сбой или блокировка программы, даже переменная никогда не будет истинной

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

Ответ 7

Итерирование объектов ($.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)
        }
}

Ответ 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 находятся в аналогичном посте: fooobar.com/questions/286016/...

Ответ 9

Как и в ответе Светобород, я использую следующий подход

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
}

Ответ 10

Я попытался использовать подход @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);
 }

Ответ 11

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

Ответ 12

использование неблокирующего JavaScript с EventTarget API

В моем примере мне нужно дождаться обратного вызова, прежде чем использовать его. Я понятия не имею, когда этот обратный вызов установлен. Это может быть до или после того, как мне нужно выполнить его. И мне может понадобиться вызвать его несколько раз (все асинхронно)

// bus to pass event
const bus = new EventTarget();

// it magic
const waitForCallback = new Promise((resolve, reject) => {
    bus.addEventListener("initialized", (event) => {
        resolve(event.detail);
    });
});



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

Ответ 13

В моем примере я регистрирую новое значение счетчика каждую секунду:

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

Ответ 14

Вам просто нужно сделать:

while(NOT condition you want)
{
wait(10)
}