[javascript] 비동기 / 대기 함수 호출

내가 이해하는 한, ES7 / ES2016에서 다중 await코드를 코드에 넣는 .then()것은 약속 과 체인을 연결 하는 것과 유사하게 작동하며 , 이는 팔러 렐이 아닌 차례로 실행됩니다. 예를 들어 다음 코드가 있습니다.

await someCall();
await anotherCall();

완료 anotherCall()되었을 때만 호출 되는 것을 올바르게 이해 someCall()합니까? 그것들을 병렬로 호출하는 가장 우아한 방법은 무엇입니까?

Node에서 사용하고 싶습니다. 비동기 라이브러리가있는 솔루션이 있습니까?

편집 : 나는이 질문에 제공된 솔루션에 만족하지 않습니다 : 비동기 생성기에서 약속을 병렬로 대기하지 않기 때문에 속도 저하 : 생성기 를 사용하고보다 일반적인 사용 사례에 대해 묻고 있습니다.



답변

당신은 기다릴 수 있습니다 Promise.all():

await Promise.all([someCall(), anotherCall()]);

결과를 저장하려면

let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

참고 Promise.all빠른 실패 수단이 곧 공급 약속 중 하나가 거부만큼, 다음 전체 물건 거부합니다.

const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

Promise.all([happy('happy', 100), sad('sad', 50)])
  .then(console.log).catch(console.log) // 'sad'

대신, 모든 약속이 이행 또는 거부 될 때까지 기다리려면을 사용할 수 있습니다 Promise.allSettled. Internet Explorer는 기본적으로이 방법을 지원하지 않습니다.

const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

Promise.allSettled([happy('happy', 100), sad('sad', 50)])
  .then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }]


답변

TL; DR

Promise.all병렬 함수 호출에 사용 하면 오류가 발생할 때 응답 동작이 올바르게 수행되지 않습니다.


먼저 모든 비동기 호출을 한 번에 실행하고 모든Promise 오브젝트를 확보하십시오 . 둘째, 물체에 사용 await하십시오 Promise. 이렇게하면 첫 번째 Promise해결 을 기다리는 동안 다른 비동기 호출이 계속 진행됩니다. 전반적으로 가장 느린 비동기 호출이있는 한 대기합니다. 예를 들면 다음과 같습니다.

// Begin first call and store promise without waiting
const someResult = someCall();

// Begin second call and store promise without waiting
const anotherResult = anotherCall();

// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];

// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise

JSbin 예 : http://jsbin.com/xerifanima/edit?js,console

주의 사항 : 모든 비동기 호출 await 첫 번째 await호출이 발생하는 호출이 동일한 회선 또는 다른 회선에 있는지 여부는 중요하지 않습니다 . JohnnyHK의 의견을 참조하십시오.


업데이트 : 이 답변은 @bergi의 답변 에 따라 오류 처리의 타이밍이 다르며 오류가 발생할 때 오류가 발생하지 않지만 모든 약속이 실행 된 후에 오류가 발생 하지 않습니다 . @jonny의 팁과 결과를 비교합니다. [result1, result2] = Promise.all([async1(), async2()]), 다음 코드 스 니펫을 확인하십시오.

const correctAsync500ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 500, 'correct500msResult');
  });
};

const correctAsync100ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 100, 'correct100msResult');
  });
};

const rejectAsync100ms = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, 'reject100msError');
  });
};

const asyncInArray = async (fun1, fun2) => {
  const label = 'test async functions in array';
  try {
    console.time(label);
    const p1 = fun1();
    const p2 = fun2();
    const result = [await p1, await p2];
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

const asyncInPromiseAll = async (fun1, fun2) => {
  const label = 'test async functions with Promise.all';
  try {
    console.time(label);
    let [value1, value2] = await Promise.all([fun1(), fun2()]);
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

(async () => {
  console.group('async functions without error');
  console.log('async functions without error: start')
  await asyncInArray(correctAsync500ms, correctAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
  console.groupEnd();

  console.group('async functions with error');
  console.log('async functions with error: start')
  await asyncInArray(correctAsync500ms, rejectAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
  console.groupEnd();
})();


답변

최신 정보:

원래의 대답은 약속 거부를 올바르게 처리하는 것을 어렵게하고 때로는 불가능한 경우도 있습니다. 올바른 해결책은 다음을 사용하는 것입니다 Promise.all.

const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

원래 답변 :

둘 중 하나를 기다리기 전에 두 함수를 모두 호출하십시오.

// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();

// Await both promises    
const someResult = await somePromise;
const anotherResult = await anotherPromise;


답변

Promise.all ()없이 병렬로 수행하는 다른 방법이 있습니다.

먼저 숫자를 인쇄하는 두 가지 기능이 있습니다.

function printNumber1() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number1 is done");
      resolve(10);
      },1000);
   });
}

function printNumber2() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number2 is done");
      resolve(20);
      },500);
   });
}

이것은 순차적입니다.

async function oneByOne() {
   const number1 = await printNumber1();
   const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done

이것은 병렬입니다 :

async function inParallel() {
   const promise1 = printNumber1();
   const promise2 = printNumber2();
   const number1 = await promise1;
   const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done


답변

이는 Promise.allSettled () 로 수행 할 수 있으며 , 이는 Promise.all()페일-패스트 동작 과 유사 하지만 실패하지 않습니다.

async function failure() {
    throw "Failure!";
}

async function success() {
    return "Success!";
}

const [failureResult, successResult] = await Promise.allSettled([failure(), success()]);

console.log(failureResult); // {status: "rejected", reason: "Failure!"}
console.log(successResult); // {status: "fulfilled", value: "Success!"}

참고 : 나는 그래서이 제한 브라우저를 지원하는 최첨단 기능입니다 강력하게 이 기능을위한 polyfill을 포함하는 것이 좋습니다.


답변

내가 만든 요지 결과, 해결 약속의 몇 가지 다른 방법을 테스트합니다. 작동하는 옵션을 보는 것이 도움이 될 수 있습니다.


답변

    // A generic test function that can be configured 
    // with an arbitrary delay and to either resolve or reject
    const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
        console.log(`Done ${ delay }`);
        resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
    }, delay));

    // Our async handler function
    const handler = async () => {
        // Promise 1 runs first, but resolves last
        const p1 = test(10000, true);
        // Promise 2 run second, and also resolves
        const p2 = test(5000, true);
        // Promise 3 runs last, but completes first (with a rejection) 
        // Note the catch to trap the error immediately
        const p3 = test(1000, false).catch(e => console.log(e));
        // Await all in parallel
        const r = await Promise.all([p1, p2, p3]);
        // Display the results
        console.log(r);
    };

    // Run the handler
    handler();
    /*
    Done 1000
    Reject 1000
    Done 5000
    Done 10000
    */

p1, p2 및 p3을 설정하면 병렬로 엄격하게 실행되지는 않지만 실행을 유지하지 않으며 컨텍스트 오류를 ​​catch로 잡을 수 있습니다.