[javascript] 함수를 다시 시작하기 전에 JavaScript Promise가 해결되기를 기다리는 방법은 무엇입니까?

단위 테스트를하고 있습니다. 테스트 프레임 워크는 페이지를 iFrame에로드 한 다음 해당 페이지에 대해 어설 션을 실행합니다. 각 테스트가 시작되기 전에 를 호출 Promise할 iFrame의 onload이벤트를 resolve()설정하고 iFrame의을 설정 src하고 promise를 반환 하는를 만듭니다.

따라서을 호출 loadUrl(url).then(myFunc)하면 무엇이든지 실행하기 전에 페이지가로드 될 때까지 기다립니다 myFunc.

주로 DOM 변경을 허용하기 위해 (예 : 버튼 클릭을 흉내 내고 div가 숨기고 표시 될 때까지 기다릴 때까지) 내 테스트 (URL로드뿐만 아니라) 전체에서 이런 종류의 패턴을 사용합니다.

이 디자인의 단점은 몇 줄의 코드로 익명 함수를 계속 작성하고 있다는 것입니다. 또한 해결 방법 (QUnit assert.async())이 있지만 약속을 정의하는 테스트 함수는 약속이 실행되기 전에 완료됩니다.

Promise.NET의 .NET과 유사하게 해결 될 때까지 또는 대기 (블록 / 슬립) 에서 값을 가져 오는 방법이 있는지 궁금합니다 IAsyncResult.WaitHandle.WaitOne(). JavaScript가 단일 스레드라는 것을 알고 있지만 이것이 함수가 양보 할 수 없다는 것을 의미하지는 않기를 바랍니다.

본질적으로 올바른 순서로 결과를 뱉어 내기 위해 다음을 얻을 수있는 방법이 있습니까?

function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");

    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
};

function getResultFrom(promise) {
  // todo
  return " end";
}

var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>



답변

.NET의 IAsyncResult.WaitHandle.WaitOne ()과 유사하게 Promise에서 값을 가져 오거나 해결 될 때까지 대기 (차단 / 절전) 할 수있는 방법이 있는지 궁금합니다. 자바 스크립트가 단일 스레드라는 것을 알고 있지만 이것이 함수가 양보 할 수 없다는 것을 의미하지는 않기를 바랍니다.

브라우저에서 자바 스크립트의 현재 세대는없는 wait()또는 sleep()그 실행에 다른 일을 할 수 있습니다. 그래서 당신은 단순히 당신이 요구하는 것을 할 수 없습니다. 대신, 작업을 수행 한 다음 완료되면 호출하는 비동기 작업이 있습니다 (Promise를 사용했던 것처럼).

이것의 일부는 Javascript의 단일 스레드 성 때문입니다. 단일 스레드가 회전 중이면 해당 회전 스레드가 완료 될 때까지 다른 Javascript를 실행할 수 없습니다. ES6는 yield이와 같은 협력적인 트릭을 허용하는 생성기를 도입 했지만, 설치된 브라우저의 광범위한 견본에서 사용할 수있는 방법은 상당히 많습니다 (JS 엔진을 제어하는 ​​서버 측 개발에서 사용할 수 있습니다) 사용중인).


약속 기반 코드를주의 깊게 관리하면 많은 비동기 작업의 실행 순서를 제어 할 수 있습니다.

코드에서 달성하려는 순서를 정확히 이해하지 못했지만 기존 kickOff()함수를 사용하여 이와 같은 작업을 수행 한 다음 .then()호출 한 후 처리기를 연결할 수 있습니다.

function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");

    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
}

kickOff().then(function(result) {
    // use the result here
    $("#output").append(result);
});

다음과 같이 보증 된 순서로 출력을 반환합니다.

start
middle
end

2018 년 업데이트 (이 답변이 작성된 후 3 년) :

당신도 당신의 코드를 transpile 또는 지원 ES7 같은 기능을한다는 환경에서 코드를 실행하는 경우 asyncawait, 당신은 지금 사용할 수있는 await코드가 “표시”할 약속의 결과를 기다려야한다. 그것은 여전히 ​​약속을 가지고 발전하고 있습니다. 여전히 모든 Javascript를 차단하지는 않지만 더 친숙한 구문으로 순차적 작업을 작성할 수 있습니다.

ES6 방식 대신 :

someFunc().then(someFunc2).then(result => {
    // process result here
}).catch(err => {
    // process error here
});

다음과 같이 할 수 있습니다.

// returns a promise
async function wrapperFunc() {
    try {
        let r1 = await someFunc();
        let r2 = await someFunc2(r1);
        // now process r2
        return someValue;     // this will be the resolved value of the returned promise
    } catch(e) {
        console.log(e);
        throw e;      // let caller know the promise was rejected with this reason
    }
}

wrapperFunc().then(result => {
    // got final result
}).catch(err => {
    // got error
});


답변

ES2016을 사용하는 경우 사용할 수 있습니다 asyncawait과 같은 것을 할 :

(async () => {
  const data = await fetch(url)
  myFunc(data)
}())

ES2015를 사용하는 경우 Generators 를 사용할 수 있습니다 . 구문이 마음에 들지 않으면 여기에 설명 된 대로 async유틸리티 함수 를 사용하여 추상화 할 수 있습니다 .

ES5를 사용하는 경우 Bluebird 와 같은 라이브러리가 더 많은 제어를 제공하기를 원할 것입니다 .

마지막으로 런타임이 ES2015를 지원하는 경우 이미 실행 순서는 Fetch Injection을 사용하여 병렬 처리로 보존 될 수 있습니다 .


답변

또 다른 옵션은 Promise.all을 사용하여 일련의 프라 미스가 해결 될 때까지 기다리는 것입니다. 아래 코드는 모든 약속이 해결 될 때까지 기다린 다음 모든 준비가 완료되면 결과를 처리하는 방법을 보여줍니다 (질문의 목적인 것처럼 보임). 그러나 start는 resolve를 호출하기 전에 해당 코드를 추가하는 것만으로 middle이 해결되기 전에 분명히 출력 할 수 있습니다.

function kickOff() {
  let start = new Promise((resolve, reject) => resolve("start"))
  let middle = new Promise((resolve, reject) => {
    setTimeout(() => resolve(" middle"), 1000)
  })
  let end = new Promise((resolve, reject) => resolve(" end"))

  Promise.all([start, middle, end]).then(results => {
    results.forEach(result => $("#output").append(result))
  })
}

kickOff()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>


답변

수동으로 할 수 있습니다. (그게 좋은 해결책은 아니지만 ..) 값이 없을 while때까지 루프를 사용하십시오.result

kickOff().then(function(result) {
   while(true){
       if (result === undefined) continue;
       else {
            $("#output").append(result);
            return;
       }
   }
});


답변