Bayrak = true olana kadar bekleyin


96

Bunun gibi javascript işlevim var:

function myFunction(number) {

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

    ...
    ... do something

}

Sorun şu ki, javascript bu süre içinde takılı kaldı ve programımı sıkıştı. Öyleyse sorum şu: "meşgul bekleme" olmadan bayrak doğru olana kadar işlevin ortasında nasıl bekleyebilirim?


3
Senin başlatımlar için söz kalıbı kullanın - oldukça uzun bir kütüphanelerde bulunabilir mi jQuery.Deferred, Q, async, ...
Sirko

tam olarak nerede ve nasıl kullanılır?
ilay zeidman

1
Çeşitli kütüphanelerin vaat edilen uygulamalarını açıklayan birçok öğretici vardır, örn. jQuery.Deferred veya S . Btw, senin temel problemin bu sorudakiyle aynı .
Sirko

3
2018'de bunu okuyan biri için Promises, opera mini ve IE11'in yanı sıra tüm tarayıcılar tarafından desteklenmektedir.
Daniel Reina

Ana sorun, olaylara bağlı tek iş parçacıklı js'de gerçekten engelleme (uyku) beklemenin imkansız olmasıdır. Yalnızca bekleme işleyicisi oluşturabilirsiniz. daha fazlasını görün: stackoverflow.com/questions/41842147/…
SalientBrain

Yanıtlar:


74

Bir tarayıcıdaki javascript tek iş parçacıklı olduğundan (burada yer almayan web çalışanları hariç) ve bir javascript yürütme iş parçacığı, diğerinin çalışmadan önce tamamlanana kadar çalıştığı için, ifadeniz:

while(flag==false) {}

sonsuza kadar çalışacak (veya tarayıcı yanıt vermeyen bir javascript döngüsünden şikayet edene kadar), sayfa asılı görünecek ve başka hiçbir javascript çalıştırma şansı bulamayacak, bu nedenle bayrağın değeri asla değiştirilemez.

Biraz daha açıklama için, Javascript olay odaklı bir dildir . Bu, kontrolü yorumlayıcıya geri döndürene kadar bir Javascript parçası çalıştırdığı anlamına gelir. Ardından, yalnızca yorumlayıcıya geri döndüğünde, Javascript olay kuyruğundan bir sonraki olayı alır ve çalıştırır.

Zamanlayıcılar ve ağ olayları gibi her şey olay kuyruğunda çalışır. Dolayısıyla, bir zamanlayıcı çalıştırıldığında veya bir ağ isteği geldiğinde, o anda çalışan Javascript'i hiçbir zaman "kesintiye uğratmaz". Bunun yerine, Javascript olay kuyruğuna bir olay yerleştirilir ve daha sonra, o anda çalışan Javascript bittiğinde, bir sonraki olay olay kuyruğundan çekilir ve çalışma sırası gelir.

Yani, sonsuz bir döngü yaptığınızda while(flag==false) {}, şu anda çalışan Javascript asla bitmez ve bu nedenle bir sonraki olay olay kuyruğundan asla çekilmez ve dolayısıyla değeri flagasla değişmez. Buradaki anahtar, Javascript'in kesintiye dayalı olmamasıdır . Bir zamanlayıcı çalıştığında, o anda çalışan Javascript'i kesintiye uğratmaz, başka bir Javascript çalıştırır ve ardından o anda çalışan Javascript'in devam etmesine izin vermez. Sadece olay kuyruğuna konur ve o anda çalışan Javascript çalışma sırasının gelmesini bekleyin.


Yapmanız gereken, kodunuzun nasıl çalıştığını yeniden düşünmek ve flagdeğer değiştiğinde çalıştırmak istediğiniz kodu tetiklemenin farklı bir yolunu bulmaktır . Javascript, olay odaklı bir dil olarak tasarlanmıştır. Yani yapmanız gereken şey, ilgilendiğiniz olayları kaydedebileceğiniz bulmaktır, böylece ya bayrağın değişmesine neden olabilecek olayı dinleyebilir ve o olaydaki bayrağı inceleyebilir ya da kendi etkinliğinizi başlatabilirsiniz. hangi kod bayrağı değiştirirse değiştirsin veya bayrak değerini değiştirmekten sorumlu kod parçası değerini değiştirdiğinde bu bayrak ne olursa olsun geri çağrınızı çağırabilecek bir geri arama işlevi uygulayabilirsiniz true, yalnızca geri arama işlevini ve dolayısıyla kodunuzu çağırır bayrak ayarlandığında çalıştırmak isteyentruedoğru zamanda çalıştırılacak. Bu, bayrak değerini sürekli kontrol etmek için bir çeşit zamanlayıcı kullanmaya çalışmaktan çok, çok daha etkilidir.

function codeThatMightChangeFlag(callback) {
    // do a bunch of stuff
    if (condition happens to change flag value) {
        // call the callback to notify other code
        callback();
    }
}

99

Javascript tek iş parçacıklıdır, dolayısıyla sayfa engelleme davranışı vardır. Başkaları tarafından önerilen ertelenmiş / sözlü yaklaşımı kullanabilirsiniz, ancak en temel yol kullanmak olacaktır window.setTimeout. Örneğin

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

İşte daha fazla açıklama içeren iyi bir öğretici: Öğretici

DÜZENLE

Başkalarının da belirttiği gibi, en iyi yol, geri aramaları kullanmak için kodunuzu yeniden yapılandırmaktır. Bununla birlikte, bu cevap size zaman uyumsuz bir davranışı nasıl 'simüle edebileceğiniz' konusunda bir fikir vermelidir window.setTimeout.


1
Bir yandan bu yanıtı gerçekten beğendim, çünkü gerçekten bir js 'bekle', bir değer döndürmek istiyorsanız o kadar kullanışlı olmaz. Bir değer döndürmezseniz, model için gerçek bir dünya kullanım durumu olduğundan o kadar emin değilim?
Martin Meeser

Elbette bir söz verebilir ve işlevi bu şekilde uygulayabilirsiniz. Ancak bu, ECMA-262 kullanmadığınız sürece, genellikle sözler veya çoklu doldurma uygulayan bir üçüncü taraf kitaplığı gerektirir. Bir söze karşılık vermeden, en iyi yol, arayan kişiye bir sonucun mevcut olduğunu bildirmek için bir geri arama mekanizması kullanmaktır.
Kiran

Gerekirse parametreleri de iletebilirsiniz: stackoverflow.com/questions/1190642/…
SharpC

1
Bu harika bir cevap. Pek çok teknoloji forumunu araştırdıktan sonra neredeyse pes ediyordum. Bence mükemmel bir şekilde işe yaradı çünkü bir değeri geri getiriyordum. Aynı zamanda Internet Explorer'da da çalıştı, bir grup teşekkürler.
Joseph

19

Promise , async \ await ve EventEmitter kullanarak herhangi bir döngü olmadan bayrak değişikliğine anında tepki vermeyi sağlayan çözüm

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

EventEmitterdüğümde yerleşiktir. Tarayıcıya kendi başınıza eklemeniz gerekir, örneğin bu paketi kullanarak: https://www.npmjs.com/package/eventemitter3


17

Async / Await özellikli 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
İnsanlar bunu nasıl özledi
Aviad

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

Kullanım:

waitFor(() => window.waitForMe, () => console.log('got you'))

11

Ecma Script 2017 ile Async-await'i kullanabilirsiniz ve bunu yapmak için birlikte iken Ve program çökmeyecek veya kilitlenmeyecek, değişken bile asla doğru olmayacaktır

//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 kullanarak modern çözüm

myFunction() orijinal soruda aşağıdaki gibi değiştirilebilir

async function myFunction(number) {

    var x=number;
    ...
    ... more initializations

    await until(_ => flag == true);

    ...
    ... do something

}

until()bu yardımcı program işlevi nerede

function until(conditionFunction) {

  const poll = resolve => {
    if(conditionFunction()) resolve();
    else setTimeout(_ => poll(resolve), 400);
  }

  return new Promise(poll);
}

Async / await ve ok işlevlerine bazı referanslar benzer bir gönderide: https://stackoverflow.com/a/52652681/209794


4

($ .Each) nesnelerini yinelemek ve her nesne üzerinde uzun süre çalışan bir işlemi (yuvalanmış ajax senkronizasyon çağrılarını içeren) yürütmek için:

Önce done=falseher biri için özel bir özellik belirledim .

Ardından, özyinelemeli bir işlevde, her birini ayarlayın done=trueve kullanmaya devam edin setTimeout. (Bu , diğer tüm kullanıcı arayüzlerini durdurmayı, bir ilerleme çubuğu göstermeyi ve diğer tüm kullanımları engellemeyi amaçlayan bir işlem , böylece senkronizasyon aramaları için kendimi affettim.)

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'ın cevabına benzer şekilde, aşağıdaki yaklaşımı kullanıyorum

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 yaklaşımını aşağıdaki gibi kullanmaya çalıştım:

checkFlag: function() {
  var currentObject = this; 
  if(flag == false) {
      setTimeout(currentObject.checkFlag, 100); 
   } else {
     /* do something*/
   }
}

(kullandığım çerçeve beni işlevleri bu şekilde tanımlamaya zorluyor). Ancak başarılı olmadan checkFlag işlevi ikinci kez çalıştırıldığında thisbenim amacım değil Window. Bu yüzden aşağıdaki kodu bitirdim

checkFlag: function() {
    var worker = setInterval (function(){
         if(flag == true){             
             /* do something*/
              clearInterval (worker);
         } 
    },100);
 }

1

EventTarget API ile engellemeyen javascript kullanma

Örneğimde, kullanmadan önce bir geri aramayı beklemem gerekiyor. Bu geri aramanın ne zaman ayarlandığına dair hiçbir fikrim yok. Yürütmem gerekenden önce veya sonra olabilir. Ve onu birkaç kez aramam gerekebilir (her şey eşzamansız)

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

delaykullanımı çok kolay bir düğüm paketi var

const delay = require('delay');

(async () => {
    bar();

    await delay(100);

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

1

Kullanmanıza izin veriliyorsa: async/awaitkodunuzda şunu deneyebilirsiniz:

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

Demo burada: https://stackblitz.com/edit/typescript-bgtnhj?file=index.ts

Konsolunda, sadece kopyalama / yapıştırın: goahead = true.


1

Buradaki geri arama çözümleri doğrultusunda bir yaklaşım benimsedim, ancak biraz daha genel hale getirmeye çalıştım. Buradaki fikir, kuyruğa bir şey değiştikten sonra yürütmeniz gereken işlevleri eklemenizdir. Bir şey olduğunda, daha sonra kuyrukta döngüde ilerlersiniz, işlevleri çağırır ve kuyruğu boşaltabilirsiniz.

Sıraya işlev ekle:

let _queue = [];

const _addToQueue = (funcToQ) => {
    _queue.push(funcToQ);
}

Sırayı yürütün ve temizleyin:

const _runQueue = () => {
    if (!_queue || !_queue.length) {
        return;
    }

    _queue.forEach(queuedFunc => {
        queuedFunc();
    });

    _queue = [];
}

Ve _addToQueue'yu çağırdığınızda, geri aramayı sarmalamak isteyeceksiniz:

_addToQueue(() => methodYouWantToCallLater(<pass any args here like you normally would>));

Koşulu karşıladığınızda arayın _runQueue()

Bu benim için faydalı oldu çünkü aynı durumda beklemem gereken birkaç şey vardı. Ve koşulun tespitini, bu koşula ulaşıldığında yapılması gerekenden ayırır.


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

Örneğimde, her saniye yeni bir sayaç değeri kaydediyorum:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.