Tunggu sampai flag = true


96

Saya memiliki fungsi javascript seperti ini:

function myFunction(number) {

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

    ...
    ... do something

}

Masalahnya adalah bahwa javascript macet dan program saya macet. jadi pertanyaan saya adalah bagaimana saya bisa menunggu di tengah fungsi sampai flag benar tanpa "busy-wait"?


3
Menggunakan pola janji untuk inisialisasi Anda - dapat ditemukan di beberapa perpustakaan seperti jQuery.Deferred, Q, async, ...
Sirko

di mana tepatnya menggunakannya dan bagaimana?
ilay zeidman

1
Ada banyak tutorial seputar menjelaskan implementasi promise dari berbagai library, misalnya. jQuery.Deferred atau Q . Btw, masalah mendasar Anda sama dengan pertanyaan ini .
Sirko

3
Untuk seseorang yang membaca ini di tahun 2018, Promises didukung oleh semua browser selain opera mini dan IE11.
Daniel Reina

Masalah utama adalah bahwa tidak mungkin untuk melakukan pemblokiran (tidur) menunggu yang benar-benar diven di js utas tunggal. Anda hanya dapat membuat penangan menunggu. lihat lebih lanjut: stackoverflow.com/questions/41842147/…
SalientBrain

Jawaban:


74

Karena javascript di browser adalah single thread (kecuali untuk webworkers yang tidak terlibat di sini) dan satu thread eksekusi javascript berjalan sampai selesai sebelum yang lain dapat berjalan, pernyataan Anda:

while(flag==false) {}

hanya akan berjalan selamanya (atau sampai browser mengeluh tentang loop javascript yang tidak responsif), halaman akan tampak digantung dan tidak ada javascript lain yang akan mendapatkan kesempatan untuk berjalan, sehingga nilai bendera tidak akan pernah dapat diubah.

Untuk penjelasan lebih lanjut, Javascript adalah bahasa yang digerakkan oleh peristiwa . Itu berarti bahwa ia menjalankan sepotong Javascript sampai ia mengembalikan kendali kembali ke penerjemah. Kemudian, hanya ketika kembali ke interpreter, Javascript mendapatkan event berikutnya dari antrian event dan menjalankannya.

Semua hal seperti pengatur waktu dan acara jaringan dijalankan melalui antrean acara. Jadi, ketika pengatur waktu aktif atau permintaan jaringan tiba, pengatur waktu tidak akan pernah "mengganggu" Javascript yang sedang berjalan. Alih-alih, sebuah acara dimasukkan ke dalam antrean acara Javascript dan kemudian, saat Javascript yang sedang berjalan selesai, acara berikutnya ditarik dari antrean acara dan gilirannya untuk dijalankan.

Jadi, ketika Anda melakukan pengulangan tak terbatas seperti while(flag==false) {}, Javascript yang sedang berjalan tidak pernah selesai dan dengan demikian kejadian berikutnya tidak pernah ditarik dari antrian kejadian dan dengan demikian nilai flagtidak pernah berubah. Kuncinya di sini adalah bahwa Javascript tidak didorong oleh interupsi . Ketika pengatur waktu menyala, itu tidak mengganggu Javascript yang sedang berjalan, menjalankan beberapa Javascript lain dan kemudian membiarkan Javascript yang sedang berjalan melanjutkan. Itu hanya dimasukkan ke dalam antrian acara menunggu sampai Javascript yang sedang berjalan selesai untuk mendapatkan gilirannya untuk dijalankan.


Yang perlu Anda lakukan adalah memikirkan kembali cara kerja kode Anda dan menemukan cara berbeda untuk memicu kode apa pun yang ingin Anda jalankan saat flagnilainya berubah. Javascript dirancang sebagai bahasa yang digerakkan oleh peristiwa. Jadi, yang perlu Anda lakukan adalah mencari tahu peristiwa apa yang Anda minati sehingga Anda dapat mendengarkan peristiwa yang mungkin menyebabkan perubahan bendera dan Anda dapat memeriksa bendera pada peristiwa itu atau Anda dapat memicu peristiwa Anda sendiri dari kode apa pun yang mungkin mengubah bendera atau Anda dapat mengimplementasikan fungsi panggilan balik bahwa kode apa pun yang berubah bendera itu dapat memanggil callback Anda setiap kali bagian kode yang bertanggung jawab untuk mengubah nilai bendera akan mengubah nilainya menjadi true, itu hanya memanggil fungsi panggilan balik dan dengan demikian kode Anda yang ingin dijalankan saat bendera disetel ketrueakan dijalankan pada waktu yang tepat. Ini jauh lebih efisien daripada mencoba menggunakan semacam pengatur waktu untuk terus-menerus memeriksa nilai bendera.

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 adalah single threaded, karenanya merupakan perilaku pemblokiran halaman. Anda dapat menggunakan pendekatan ditangguhkan / janji yang disarankan oleh orang lain, tetapi cara yang paling dasar adalah menggunakan window.setTimeout. Misalnya

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

Berikut adalah tutorial yang bagus dengan penjelasan lebih lanjut: Tutorial

EDIT

Seperti yang ditunjukkan orang lain, cara terbaik adalah menyusun ulang kode Anda untuk menggunakan callback. Namun, jawaban ini akan memberi Anda gambaran bagaimana Anda dapat 'mensimulasikan' perilaku asynchronous dengan window.setTimeout.


1
Sementara di satu sisi saya sangat menyukai jawaban ini karena memang sebuah js 'tunggu' itu menjadi tidak berguna jika Anda ingin mengembalikan nilai. Jika Anda tidak mengembalikan nilai, saya tidak begitu yakin ada kasus penggunaan dunia nyata untuk pola tersebut?
Martin Meeser

Anda tentu saja dapat mengembalikan janji dan mengimplementasikan fungsi dengan cara itu. Namun ini biasanya memerlukan pustaka pihak ketiga yang mengimplementasikan janji atau polyfill, kecuali Anda menggunakan ECMA-262. Tanpa mengembalikan janji, cara terbaik adalah menggunakan mekanisme panggilan balik untuk memberi sinyal kepada pemanggil bahwa hasilnya tersedia.
Kiran

Anda juga dapat mengirimkan parameter jika diperlukan: stackoverflow.com/questions/1190642/…
SharpC

1
Ini adalah jawaban yang bagus. Saya hampir menyerah setelah menggali melalui banyak forum teknologi. Saya pikir itu bekerja dengan sempurna untuk saya karena saya mengembalikan nilai. Ini juga berfungsi di Internet Explorer juga. Terima kasih banyak.
Joseph

19

Solusi menggunakan Promise , async \ await dan EventEmitter yang memungkinkan untuk bereaksi langsung pada perubahan bendera tanpa loop sama sekali

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

EventEmitteradalah node bawaan. Di browser Anda harus memasukkannya sendiri, misalnya menggunakan paket ini: https://www.npmjs.com/package/eventemitter3


17

ES6 dengan 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)

1
Bagaimana orang melewatkan ini
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();
    }
}

Menggunakan:

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

11

Dengan Ecma Script 2017 Anda dapat menggunakan async-await dan sementara bersama-sama untuk melakukannya Dan sementara tidak akan merusak atau mengunci program bahkan variabel tidak pernah benar

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

Solusi modern menggunakan Promise

myFunction() dalam pertanyaan awal dapat dimodifikasi sebagai berikut

async function myFunction(number) {

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

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

    ...
    ... do something

}

dimana until()fungsi utilitas ini

function until(conditionFunction) {

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

  return new Promise(poll);
}

Beberapa referensi ke async / await dan fungsi panah ada di pos serupa: https://stackoverflow.com/a/52652681/209794


4

Untuk melakukan iterasi lebih dari objek ($ .each) dan menjalankan operasi yang berjalan lama (berisi panggilan sinkronisasi ajax bertingkat) pada setiap objek:

Saya pertama kali menetapkan done=falseproperti khusus pada masing-masing.

Kemudian, dalam fungsi rekursif, atur masing done=true- masing dan lanjutkan menggunakan setTimeout. (Ini adalah operasi yang dimaksudkan untuk menghentikan semua UI lainnya, menampilkan bilah kemajuan dan memblokir semua penggunaan lainnya jadi saya memaafkan diri saya sendiri untuk panggilan sinkronisasi.)

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

Mirip dengan jawaban Lightbeard, saya menggunakan pendekatan berikut

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

Saya mencoba menggunakan pendekatan @Kiran seperti berikut:

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

(kerangka kerja yang saya gunakan memaksa saya untuk mendefinisikan fungsi dengan cara ini). Tetapi tidak berhasil karena ketika eksekusi masuk ke dalam fungsi checkFlag kedua kalinya, thisitu bukan objek saya Window. Jadi, saya selesai dengan kode di bawah ini

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

1

menggunakan javascript non blocking dengan EventTarget API

Dalam contoh saya, saya harus menunggu panggilan balik sebelum menggunakannya. Saya tidak tahu kapan panggilan balik ini disetel. Bisa sebelum atau sesudah saya harus menjalankannya. Dan saya perlu menyebutnya beberapa kali (semuanya asinkron)

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

ada paket node yang delaysangat mudah digunakan

const delay = require('delay');

(async () => {
    bar();

    await delay(100);

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

1

Jika Anda diizinkan untuk menggunakan: async/awaitpada kode Anda, Anda dapat mencoba yang ini:

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 di sini: https://stackblitz.com/edit/typescript-bgtnhj?file=index.ts

Pada konsol, hanya copy / paste: goahead = true.


1

Saya mengambil pendekatan di sepanjang garis solusi panggilan balik di sini, tetapi mencoba membuatnya sedikit lebih umum. Idenya adalah Anda menambahkan fungsi yang perlu Anda jalankan setelah ada perubahan pada antrian. Ketika hal itu terjadi, Anda kemudian mengulang antrian, memanggil fungsi dan mengosongkan antrian.

Tambahkan fungsi ke antrian:

let _queue = [];

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

Jalankan dan hapus antrian:

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

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

    _queue = [];
}

Dan saat Anda memanggil _addToQueue, Anda akan ingin menggabungkan callback:

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

Bila Anda sudah memenuhi syarat, hubungi _runQueue()

Ini berguna bagi saya karena saya memiliki beberapa hal yang perlu menunggu dalam kondisi yang sama. Dan itu memisahkan deteksi kondisi dari apa pun yang perlu dijalankan saat kondisi itu tercapai.


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

Dalam contoh saya, saya mencatat nilai penghitung baru setiap detik:

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.