많은 약속을 반환하고 다른 작업을 수행하기 전에 모두 기다리는 방법


85

비동기 적으로 작업을 수행하는 메서드를 호출하는 루프가 있습니다. 이 루프는 메서드를 여러 번 호출 할 수 있습니다. 이 루프 이후에는 모든 비동기 작업이 완료 될 때만 실행해야하는 또 다른 루프가 있습니다.

그래서 이것은 내가 원하는 것을 보여줍니다.

for (i = 0; i < 5; i++) {
    doSomeAsyncStuff();    
}

for (i = 0; i < 5; i++) {
    doSomeStuffOnlyWhenTheAsyncStuffIsFinish();    
}

나는 약속에별로 익숙하지 않은데 누구든지 나를 도와 줄 수 있습니까?

이것이 내 doSomeAsyncStuff()행동입니다.

function doSomeAsyncStuff() {
    var editor = generateCKEditor();
    editor.on('instanceReady', function(evt) {
        doSomeStuff();
        // There should be the resolve() of the promises I think.
    })
}

아마도 다음과 같이해야합니다.

function doSomeAsyncStuff() {
    var editor = generateCKEditor();
    return new Promise(function(resolve,refuse) {
        editor.on('instanceReady', function(evt) {
            doSomeStuff();
            resolve(true);
        });
    });
}

그러나 구문이 확실하지 않습니다.


비동기 호출을 제어하고 있습니까? 그들은 이미 약속을 반환합니까, 아니면 약속을 반환하도록 할 수 있습니까?
TJ Crowder

시퀀스는 정확히 무엇입니까? 이전 비동기 함수가 모두 완료된 후 다른 함수를 호출해야 합니까? 아니면 각 비동기가 완료된 후 함수를 호출해야합니까?
Sosdoc jul.

지금은 첫 번째 함수가 약속을 반환하지 않습니다. 구현해야합니다. 내 기능의 워크 플로에 대한 세부 정보를 추가하기 위해 메시지를 편집하고 싶습니다. 그리고 예, 두 번째 루프의 항목을 실행하기 전에 첫 번째 루프의 모든 항목이 완료되어야합니다.
Ganbin

1
다시 편집하십시오. "아마도 그런 일을해야 할 것 같습니다."네 , 아주 비슷합니다 . s하지만 끝에는 없습니다 Promise.
TJ Crowder

답변:


161

이를 위해 Promise.all( spec , MDN )을 사용할 수 있습니다 . 여러 개의 개별 약속을 수락하고 사용자가 제공 한 모든 약속이 해결 될 때 해결되는 단일 약속을 돌려주고 그중 하나가 거부되면 거부됩니다.

따라서 doSomeAsyncStuff약속 을 반환하면 :

    const promises = [];
//  ^^^^^−−−−−−−−−−−−−−−−−−−−−−−−−−− use `const` or `let`, not `var`
    
    for (let i = 0; i < 5; i++) {
//       ^^^−−−−−−−−−−−−−−−−−−−−−−−− added missing declaration
        promises.push(doSomeAsyncStuff());
    }
    
    Promise.all(promises)
        .then(() => {
            for (let i = 0; i < 5; i++) {
//               ^^^−−−−−−−−−−−−−−−− added missing declaration
                doSomeStuffOnlyWhenTheAsyncStuffIsFinish();    
            }
        })
        .catch((e) => {
            // handle errors here
        });

MDN에는 여기 에 약속에 대한 기사가 있습니다 . 또한 제 책 JavaScript : The New Toys의 8 장에서 프록시에 대해 자세히 다룹니다 .

예를 들면 다음과 같습니다.

 function doSomethingAsync(value) {
     return new Promise((resolve) => {
         setTimeout(() => {
             console.log("Resolving " + value);
             resolve(value);
         }, Math.floor(Math.random() * 1000));
     });
   }
   
   function test() {
       const promises = [];
       
       for (let i = 0; i < 5; ++i) {
           promises.push(doSomethingAsync(i));
       }
       
       Promise.all(promises)
           .then((results) => {
               console.log("All done", results);
           })
           .catch((e) => {
               // Handle errors here
           });
   }
   
   test();

샘플 출력 (으로 인해 Math.random먼저 완료되는 내용이 다를 수 있음) :

해결 3
해결 2
해결 1
해결 4
0 해결
모두 완료 [0,1,2,3,4]

감사합니다. 지금 시도해보고 몇 분 안에 피드백을받습니다.
Ganbin 2015

12
와우, 정말 고마워요, 이제 약속에 대해 훨씬 더 많이 이해했습니다. 이제 더 좋아졌고 여러분 덕분에 멋진 글을 쓸 수 있습니다.
Ganbin

1
또한 어떤 이유로 든 (예 : 진행 상황을 조롱하는) 순서대로 이러한 작업을 완료하려면 다음과 같이 변경할 수 있습니다 Math.floor(Math.random() * 1000).(i * 1000)
OK sure

@TJ는 지금은보기에 결과 데이터를 렌더링 할 수있는 방법과 내가 데이터를 보여 루프가 할 수있는
아지트 싱

1
@ user1063287-코드 await가 허용 되는 컨텍스트에 있으면 그렇게 할 수 있습니다 . 현재 사용할 수있는 유일한 장소 awaitasync함수 내부 입니다. (어떤 시점에서 당신은 또한 모듈의 최상위 레벨에서 사용할 수 있습니다.)
TJ 크라우

5

재사용 가능한 함수는이 패턴에 대해 잘 작동합니다.

function awaitAll(count, asyncFn) {
  const promises = [];

  for (i = 0; i < count; ++i) {
    promises.push(asyncFn());
  }

  return Promise.all(promises);
}

OP 예 :

awaitAll(5, doSomeAsyncStuff)
  .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))
  .catch(e => console.error(e));

관련 패턴은 배열을 반복하고 각 항목에 대해 비동기 작업을 수행합니다.

function awaitAll(list, asyncFn) {
  const promises = [];

  list.forEach(x => {
    promises.push(asyncFn(x));
  });

  return Promise.all(promises);
}

예:

const books = [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }];

function doSomeAsyncStuffWith(book) {
  return Promise.resolve(book.name);
}

awaitAll(books, doSomeAsyncStuffWith)
  .then(results => console.log('doSomeStuffOnlyWhenTheAsyncStuffIsFinished', results))
  .catch(e => console.error(e));

1
이것은 코드를 이해하기 쉽고 깔끔하게 만듭니다. 나는 현재의 예제 (OP의 코드에 분명히 적응 된)가이 정의를 수행한다고 생각하지 않는다. 이것은 깔끔한 속임수입니다. 감사합니다!
Shaun Vermaak

2
const doSomeAsyncStuff = async (funcs) => {
  const allPromises = funcs.map(func => func());
  return await Promise.all(allPromises);
}

doSomeAsyncStuff([
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
  () => new Promise(resolve => setTimeout(() => resolve(), 100)),
]);

1

여기에 명시된 답변을 이해하기 위해 내가 작성한 코드가 있습니다. for 루프에 몽구스 쿼리가 있으므로 여기에을 넣어 asyncFunction대신합니다. 누구에게나 도움이되기를 바랍니다. 이 스크립트는 노드 또는 여러 Javascript 런타임에서 실행할 수 있습니다.

let asyncFunction = function(value, callback)
{
        setTimeout(function(){console.log(value); callback();}, 1000);
}



// a sample function run without promises

asyncFunction(10,
    function()
    {
        console.log("I'm back 10");
    }
);


//here we use promises

let promisesArray = [];

let p = new Promise(function(resolve)
{
    asyncFunction(20,
        function()
        {
            console.log("I'm back 20");
            resolve(20);
        }
    );
});

promisesArray.push(p);


for(let i = 30; i < 80; i += 10)
{
    let p = new Promise(function(resolve)
    {
        asyncFunction(i,
            function()
            {
                console.log("I'm back " + i);
                resolve(i);
            }
        );
    });
    promisesArray.push(p);
}


// We use Promise.all to execute code after all promises are done.

Promise.all(promisesArray).then(
    function()
    {
        console.log("all promises resolved!");
    }
)

0

/*** Worst way ***/
for(i=0;i<10000;i++){
  let data = await axios.get(
    "https://yourwebsite.com/get_my_data/"
  )
  //do the statements and operations
  //that are dependant on data
}

//Your final statements and operations
//That will be performed when the loop ends

//=> this approach will perform very slow as all the api call
// will happen in series


/*** One of the Best way ***/

const yourAsyncFunction = async (anyParams) => {
  let data = await axios.get(
    "https://yourwebsite.com/get_my_data/"
  )
  //all you statements and operations here
  //that are dependant on data
}
var promises = []
for(i=0;i<10000;i++){
  promises.push(yourAsyncFunction(i))
}
await Promise.all(promises)
//Your final statement / operations
//that will run once the loop ends

//=> this approach will perform very fast as all the api call
// will happen in parallal

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