flag = true까지 기다립니다.


94

다음과 같은 자바 스크립트 기능이 있습니다.

function myFunction(number) {

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

    ...
    ... do something

}

문제는 자바 스크립트가 잠시 동안 멈춰서 내 프로그램을 멈춘다는 것입니다. 그래서 내 질문은 "busy-wait"없이 플래그가 true가 될 때까지 함수 중간에서 어떻게 기다릴 수 있습니까?


3
당신의 초기화에 대한 약속 패턴을 사용 - 꽤 도서관에서 찾을 수 있습니다 좋아 jQuery.Deferred, Q, async, ...
Sirko

정확히 어디에서 어떻게 사용합니까?
ilay zeidman 2014 년

1
다양한 라이브러리의 promise 구현을 설명하는 튜토리얼이 많이 있습니다. jQuery.Deferred 또는 Q . Btw, 귀하의 근본적인 문제는 이 질문 과 동일 합니다.
Sirko 2014 년

3
2018 년이 글을 읽는 사람에게 Promises는 Opera mini와 IE11을 제외한 모든 브라우저에서 지원됩니다.
Daniel Reina

주된 문제는 이벤트가 나간 단일 스레드 js에서 진정한 차단 (휴면) 대기를 수행 할 수 없다는 것입니다. 대기 핸들러 만 만들 수 있습니다. 더보기 : stackoverflow.com/questions/41842147/…
SalientBrain

답변:


73

브라우저의 자바 스크립트는 단일 스레드 (여기에 관련되지 않은 웹 작업자 제외)이고 자바 스크립트 실행의 한 스레드가 다른 스레드가 실행되기 전에 완료되기 때문에 다음과 같은 명령문이 있습니다.

while(flag==false) {}

단순히 영원히 실행되고 (또는 브라우저가 응답하지 않는 자바 스크립트 루프에 대해 불평 할 때까지) 페이지가 중단 된 것처럼 보이고 다른 자바 스크립트가 실행될 기회가 없으므로 플래그의 값을 변경할 수 없습니다.

좀 더 자세한 설명을 위해 Javascript는 이벤트 기반 언어 입니다. 즉, 인터프리터에게 제어권을 반환 할 때까지 Javascript를 실행합니다. 그런 다음 인터프리터로 돌아올 때만 Javascript는 이벤트 큐에서 다음 이벤트를 가져 와서 실행합니다.

타이머 및 네트워크 이벤트와 같은 모든 것은 이벤트 큐를 통해 실행됩니다. 따라서 타이머가 실행되거나 네트워크 요청이 도착하면 현재 실행중인 자바 스크립트를 "중단"하지 않습니다. 대신 이벤트가 Javascript 이벤트 큐에 들어간 다음 현재 실행중인 Javascript가 완료되면 다음 이벤트를 이벤트 큐에서 가져 와서 실행할 차례를 가져옵니다.

따라서와 같은 무한 루프를 수행 while(flag==false) {}하면 현재 실행중인 Javascript가 완료되지 않으므로 다음 이벤트가 이벤트 큐에서 flag가져 오지 않으므로의 값 이 변경되지 않습니다. 여기서 핵심은 자바 스크립트가 인터럽트 구동이 아니라는 것 입니다. 타이머가 실행될 때 현재 실행중인 자바 스크립트를 중단하지 않고 다른 자바 스크립트를 실행 한 다음 현재 실행중인 자바 스크립트를 계속합니다. 현재 실행중인 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();
    }
}

98

자바 스크립트는 단일 스레드이므로 페이지 차단 동작입니다. 다른 사람들이 제안한 지연 / 약속 접근 방식을 사용할 수 있지만 가장 기본적인 방법은 window.setTimeout. 예

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

다음은 자세한 설명이있는 좋은 튜토리얼입니다. 튜토리얼

편집하다

다른 사람들이 지적했듯이 가장 좋은 방법은 콜백을 사용하도록 코드를 재구성하는 것입니다. 그러나이 답변은 .NET을 사용하여 비동기 동작을 '시뮬레이션'할 수있는 방법을 알려줍니다 window.setTimeout.


1
한편으로는 실제로 js 'wait'이기 때문에이 답변을 정말 좋아하지만 값을 반환하려면 그렇게 사용할 수 없습니다. 값을 반환하지 않으면 패턴에 대한 실제 사용 사례가 있는지 잘 모르겠습니까?
Martin Meeser

물론 약속을 반환하고 그런 방식으로 함수를 구현할 수 있습니다. 그러나 ECMA-262를 사용하지 않는 한 일반적으로 promise 또는 polyfill을 구현하는 타사 라이브러리가 필요합니다. 약속을 반환하지 않고 가장 좋은 방법은 콜백 메커니즘을 사용하여 호출자에게 결과를 사용할 수 있음을 알리는 것입니다.
Kiran 2016

필요한 경우 매개 변수를 전달할 수도 있습니다. stackoverflow.com/questions/1190642/…
SharpC

1
이것은 훌륭한 대답입니다. 나는 많은 기술 포럼을 파헤친 후 거의 포기했습니다. 나는 가치를 반환했기 때문에 완벽하게 작동했다고 생각합니다. Internet Explorer에서도 작동했습니다.
Joseph

18

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 패키지 사용).


17

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)

1
사람이 그리워 했는가
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();
    }
}

사용하다:

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

11

Ecma Script 2017을 사용하면 async-await 및 while을 함께 사용할 수 있으며 변수가 true가 아니더라도 프로그램이 충돌하거나 잠그지 않습니다.

//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를 사용한 최신 솔루션

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 와 유사한 게시물에 있습니다.


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

1

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
}

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

1

EventTarget API 와 함께 비 차단 자바 스크립트 사용

내 예에서는 콜백을 사용하기 전에 기다려야합니다. 이 콜백이 언제 설정되었는지 모르겠습니다. 내가 그것을 실행해야하기 전일 수 있습니다. 그리고 나는 그것을 여러 번 호출해야 할 수 있습니다 (모든 비 동기화)

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

delay사용하기 매우 쉬운 노드 패키지 가 있습니다.

const delay = require('delay');

(async () => {
    bar();

    await delay(100);

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

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

동일한 조건에서 기다려야하는 몇 가지 항목이 있었기 때문에 이것은 나에게 유용했습니다. 그리고 해당 조건이 충족 될 때 실행해야하는 모든 조건에서 조건 감지를 분리합니다.


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

내 예에서는 매초마다 새 카운터 값을 기록합니다.

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


0

사용이 허용 된 경우 : 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.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.