계속하기 전에 하나의 기능이 완료되기를 기다리는 적절한 방법은 무엇입니까?


187

두 개의 JS 함수가 있습니다. 하나는 다른 하나를 호출합니다. 호출 기능 내에서 다른 기능을 호출하고 해당 기능이 완료 될 때까지 기다렸다가 계속 진행하고 싶습니다. 예를 들어 / 의사 코드 :

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};

function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

나는이 해결책을 생각해 냈지만 이것이 현명한 방법인지는 모른다.

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()
    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

합법적입니까? 그것을 처리하는 더 우아한 방법이 있습니까? 아마도 jQuery와 함께?


11
firstFunction정확히 무엇을 비 동기화합니까? 어느 쪽이든-약속을 확인하십시오
zerkms

첫 번째 기능은 매 초마다 스코어 클럭을 빠르게 업데이트하는 것입니다. ... 생각하면, setTimeout을 통해 두 번째 함수 호출을 미리 계산 한 다음 수동으로 일시 중지 할 수 있다고 가정합니다. 즉, 다른 곳에서 일시 중지 기능을 원한다는 것을 여전히 알 수 있습니다.
DA.

당신은 일시 중지가 필요 없습니다-구글 jquery에서 약속을위한
zerkms

@zerkms 약속은 재미있어 보인다! 브라우저 지원을 계속 조사 중 ...
DA.

1
나도 같은 문제가 있습니다.
Vappor Washmade

답변:


140

이와 같은 비동기 작업을 처리하는 한 가지 방법은 다음과 같은 콜백 함수를 사용하는 것입니다.

function firstFunction(_callback){
    // do some asynchronous work
    // and when the asynchronous stuff is complete
    _callback();    
}

function secondFunction(){
    // call first function and pass in a callback function which
    // first function runs when it has completed
    firstFunction(function() {
        console.log('huzzah, I\'m done!');
    });    
}

@Janaka Pushpakumara의 제안에 따라 화살표 기능을 사용하여 동일한 것을 얻을 수 있습니다. 예를 들면 다음과 같습니다.

firstFunction(() => console.log('huzzah, I\'m done!'))


업데이트 : 나는 꽤 오래 전에 이것을 대답하고 정말로 업데이트하고 싶습니다. 콜백은 절대적으로 좋지만 내 경험상 코드를 읽고 유지하기가 더 어려운 경향이 있습니다. 진행중인 이벤트 등을 매개 변수로 전달하는 등 여전히 사용하는 상황이 있습니다. 이 업데이트는 대안을 강조하기위한 것입니다.

또한 원래의 질문은 specificallty 함수가 동기 인 경우,이 때문에 경우에 누군가가 혼란, 비동기를 언급하지 않는 것입니다 전화했을 때 차단합니다. 예를 들면 다음과 같습니다.

doSomething()
// the function below will wait until doSomething completes if it is synchronous
doSomethingElse()

암시 적으로 함수가 비동기 적이지만 오늘날 모든 비동기 작업을 처리하는 방법은 async / await입니다. 예를 들면 다음과 같습니다.

const secondFunction = async () => {
  const result = await firstFunction()
  // do something else here after firstFunction completes
}

IMO, async / await는 약속을 직접 사용하는 것보다 (대부분의 경우) 코드를 훨씬 더 읽기 쉽게 만듭니다. 잡기 오류를 처리해야 할 경우 try / catch와 함께 사용하십시오. 자세한 내용은 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function에서 확인하십시오 .


9
@zerkms-정교하게 관리 하시겠습니까?
매트 웨이

7
콜백은 약속 유연한 훨씬 쉽게하고, 비동기 처리하기가 불편하다
zerkms

1
내가 가장 잘 (업데이트 된) 단어를 사용해서는 안된다는 것이 맞지만 콜백과 약속의 편의는 문제의 복잡성에 달려 있습니다.
매트 웨이

3
그것은 주제가 아니지만 개인적으로 요즘 콜백을 선호하는 이유를 보지 못합니다. :-) 약속에 대한 콜백을 제공 한 지난 2 년 동안 작성된 코드를 기억할 수 없습니다.
zerkms

3
원하는 경우 es6에서 화살표 기능을 사용할 수 있습니다. secondFunction () {firstFunction ((response) => {console.log (response);}); }
Janaka Pushpakumara

53

async / await 사용 :

async function firstFunction(){
  for(i=0;i<x;i++){
    // do something
  }
  return;
};

그런 다음 다른 함수에서 await를 사용하여 반환 될 때까지 기다리십시오.

async function secondFunction(){
  await firstFunction();
  // now wait for firstFunction to finish...
  // do something else
};

9
우리 중 오래된 브라우저를 지원하는 사람들을 위해 IE는 async / await를 지원하지 않습니다
kyle

에서와 같이 두 함수 외부에서 사용할 수 있습니까 $(function() { await firstFunction(); secondFunction(); });?
Lewistrick

1
@Lewistrick 메소드 await안에서만 사용할 수 있다는 것을 기억하십시오 async. 따라서 부모 기능 을 수행하면 원하는 것을 포함 하거나 포함하지 않는 async많은 async methods내부를 호출 할 수 있습니다 await.
Fawaz

그것은 할 수 ? awaitsetTimeout
Shayan

1
@Shayan 예, 시간 초과 후 약속을 해결하는 다른 함수로 setTimeout을 래핑합니다.
Fawaz

51

여기서 중요한 점이 누락 된 것 같습니다. JavaScript는 단일 스레드 실행 환경입니다. 코드를 다시 살펴 보겠습니다 alert("Here").

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()

    alert("Here");

    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

기다릴 필요가 없습니다 isPaused. "여기"경고 isPaused가 표시되면 false이미 firstFunction표시되어 반환됩니다. for루프 ( // do something) 내부에서 "수율"할 수 없기 때문에 루프가 중단되지 않고 먼저 완전히 완료해야합니다 (자세한 내용 : Javascript 스레드 처리 및 경쟁 조건 ).

즉, 내부에서 코드 흐름 firstFunction을 비 동기화하고 콜백을 사용하거나 발신자에게 알리겠다고 약속 할 수 있습니다. for루프 를 포기하고 if대신 ( JSFiddle )로 시뮬레이션해야합니다 .

function firstFunction()
{
    var deferred = $.Deferred();

    var i = 0;
    var nextStep = function() {
        if (i<10) {
            // Do something
            printOutput("Step: " + i);
            i++;
            setTimeout(nextStep, 500); 
        }
        else {
            deferred.resolve(i);
        }
    }
    nextStep();
    return deferred.promise();
}

function secondFunction()
{
    var promise = firstFunction();
    promise.then(function(result) { 
        printOutput("Result: " + result);
    });
}

참고로 JavaScript 1.7은 yield키워드를 생성기 의 일부로 도입했습니다 . 이를 통해 동기식 JavaScript 코드 흐름에서 비동기 홀을 "펀칭"할 수 있습니다 ( 자세한 내용 및 예 ). 그러나 생성기에 대한 브라우저 지원은 현재 Firefox 및 Chrome (AFAIK)으로 제한됩니다.


3
넌 나를 구했다. $ .Deferred ()는 내가 좋아하는 것입니다. 감사합니다
Temitayo

@noseratio 나는 이것을 시도했다. 그러나 waitForIt 메소드는 전혀 호출되지 않습니다. 내가 뭘 잘못하고 있죠?
vigamage

@ vigamage, 당신이 시도한 jsfiddle 또는 codepen에 대한 링크를 제공 할 수 있습니까?
noseratio

1
안녕하세요, 걱정 해 주셔서 감사합니다. 나는 그것을 작동시켰다. 감사합니다..!
vigamage

18

하나의 기능이 먼저 완료 될 때까지 기다리는 가장 좋은 방법은 비동기 / 대기 기능 과 함께 약속 을 사용하는 것입니다.


  1. 먼저 Promise를 만듭니다 . 내가 만든 기능은 2 초 후에 완료됩니다. setTimeout명령을 실행하는 데 시간이 걸리는 상황을 보여주기 위해 사용 했습니다.
  2. 두 번째 기능의 경우 지침을 진행하기 전에 첫 번째 기능을 완료 할 비동기 / 대기 기능을 사용할 수 있습니다 await.

예:

    //1. Create a new function that returns a promise
    function firstFunction() {
      return new Promise((resolve, reject) => {
          let y = 0
          setTimeout(() => {
            for(i=0; i<10; i++){
               y++
            }
             console.log('loop completed')  
             resolve(y)
          }, 2000)
      })
    }
    
    //2. Create an async function
    async function secondFunction() {
        console.log('before promise call')
        //3. Await for the first function to complete
        let result = await firstFunction()
        console.log('promise resolved: ' + result)
        console.log('next step')
    }; 

    secondFunction()


노트 :

당신은 할 수 단순히 과 같이 값없이 . 내 예에서, I 의 가치와 나는 다음 두 번째 기능에서 사용할 수있다.resolvePromiseresolve()resolvedPromisey


예, 이것은 항상 작동합니다. 이런 종류의 시나리오에 관련된 사람이라면 JavaScript Promise가 그 트릭을 수행합니다.
Puttamarigowda MS

5

약속의 유일한 문제는 IE가 약속을 지원하지 않는다는 것입니다. 에지는 않지만, IE 10의 많음이있다 11가 아웃 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise (하단에 호환성)

따라서 JavaScript는 단일 스레드입니다. 비동기 호출을하지 않으면 예상대로 작동합니다. 기본 JavaScript 스레드는 코드에 표시되는 순서대로 다음 함수를 실행하기 전에 하나의 함수를 완전히 실행합니다. 동기 함수의 순서를 보장하는 것은 쉽지 않습니다. 각 함수는 호출 된 순서대로 완전히 실행됩니다.

동기 함수를 원자 단위 작업으로 생각하십시오 . 기본 JavaScript 스레드는 명령문이 코드에 나타나는 순서대로이를 완전히 실행합니다.

그러나 다음 상황과 같이 비동기 호출을 처리하십시오.

showLoadingDiv(); // function 1

makeAjaxCall(); // function 2 - contains async ajax call

hideLoadingDiv(); // function 3

이것은 당신이 원하는 것을하지 않습니다 . 기능 1, 기능 2 및 기능 3을 즉시 실행합니다. div 플래시가로드되고 사라졌지 만 ajax 호출이 거의 완료되지는 않았지만 makeAjaxCall()반환되었습니다. 복잡한 것은 makeAjaxCall()주요 JavaScript 스레드의 각 스핀에 의해 조금씩 진행되는 덩어리로 작업을 나눕니다 . 비동기 적으로 작동합니다. 그러나 한 번의 스핀 / 런 동안 동일한 메인 스레드가 동기식 부분을 빠르고 예측 가능하게 실행했습니다.

그래서, 내가 그것을 다루는 방식 : 내가 말했듯이 기능은 원자 작업 단위입니다. 함수 1과 2의 코드를 결합했습니다. 비동기 호출 전에 함수 1의 코드를 함수 2에 넣습니다. 함수 1을 제거했습니다. 비동기 호출을 포함하여 모든 것을 순서대로 예측 가능하게 실행합니다.

그런 다음 비동기 호출이 완료되면 기본 JavaScript 스레드를 여러 번 회전 한 후 함수 3을 호출하게합니다 . 그러면 order가 보장됩니다 . 예를 들어, ajax를 사용하면 onreadystatechange 이벤트 핸들러가 여러 번 호출됩니다. 완료되었다고보고되면 원하는 최종 함수를 호출하십시오.

더 지저분하다는 데 동의합니다. 나는 코드가 대칭 적 인 것을 좋아하고, 함수가 한 가지 일 (또는 그에 가까운)을하는 것을 좋아하며, 어떤 식 으로든 아약스 호출이 디스플레이를 담당하는 것을 좋아하지 않습니다 (호출자에 대한 종속성 생성). 그러나 동기식 함수에 비동기 호출이 임베드 된 경우 실행 순서를 보장하려면 절충이 필요합니다. 그리고 IE 10을 코딩해야하므로 약속이 없습니다.

요약 : 동기식 호출의 경우 순서를 보장하는 것이 쉽지 않습니다. 각 함수는 호출 된 순서대로 완전히 실행됩니다. 비동기식 호출이있는 함수의 경우 순서를 보장하는 유일한 방법은 비동기식 호출이 완료되는시기를 모니터링하고 해당 상태가 감지되면 세 번째 함수를 호출하는 것입니다.

JavaScript 스레드에 대한 설명은 https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23https://developer.mozilla.org/en-US/docs/Web/JavaScript/를 참조하십시오. EventLoop

또한이 주제에 대해 비슷한 비슷한 등급의 질문이 있습니다 .3 개의 함수를 하나씩 실행하려면 어떻게해야합니까?


8
downvoter (들)에게, 나는이 답변에 무엇이 문제인지 알고 싶어합니다.
커밋

0

체인에서 여러 작업을 실행해야하기 때문에이 문제가 발생했습니다.

<button onclick="tprom('Hello Niclas')">test promise</button>

<script>
    function tprom(mess) {
        console.clear();

        var promise = new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve(mess);
            }, 2000);
        });

        var promise2 = new Promise(async function (resolve, reject) {
            await promise;
            setTimeout(function () {
                resolve(mess + ' ' + mess);
            }, 2000);
        });

        var promise3 = new Promise(async function (resolve, reject) {
            await promise2;
            setTimeout(function () {
                resolve(mess + ' ' + mess+ ' ' + mess);
            }, 2000);
        });

        promise.then(function (data) {
            console.log(data);
        });

        promise2.then(function (data) {
            console.log(data);
        });

        promise3.then(function (data) {
            console.log(data);
        });
    }

</script>

주어진 스 니펫에 대한 설명을 위해 최소한 jus에 높은 수준의 주석을 추가하십시오. 코드에서 문제를 해결하는 방법을 설명하기위한 것입니다.
Cold Cerberus

문제는 첫 번째 기능 다음에 두 번째 기능을 실행하는 것이 었습니다. 첫 번째 함수 다음에 두 번째 함수를 실행하는 방법과 두 번째 함수 다음에 세 번째 함수를 실행하는 방법을 보여줍니다. 필요한 코드를 쉽게 이해할 수 있도록 세 가지 기능을 만들었습니다.
Niclas Arnberger

0

당신의 주요 재미는 firstFun을 호출하고 완료되면 다음 재미가 호출됩니다.

async firstFunction() {
            const promise = new Promise((resolve, reject) => {
                for (let i = 0; i < 5; i++) {
                    // do something
                    console.log(i);
                    if (i == 4) {
                        resolve(i);
                    }
                }
            });
            const result = await promise;
        }

        second() {
            this.firstFunction().then( res => {
                // third function call do something
                console.log('Gajender here');
            });
        }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.