for문과 Promise 함께 쓰기

후훗♫·2020년 1월 28일
3

바닐라코딩 부트캠프 3주차 과제는 sorting함수 중 몇 가지를 골라 시각화하는 것이었다.
이 과제를 수행하면서 어려웠던 것 중 하나가
"비동기함수를 내가 원하는 시간과 순서대로 실행" 하는 것이었다.

for문을 활용하여 배열안에 담아둔 각각의 정보를 내가 구현한 함수에 전달해서
동기가 아닌 비동기적으로 실행해야 했다.

계속 실패를 하다가 우연히 stackoverflow에서 아래 링크의 글을 발견했다.
(출처 : stackoverflow) JavaScript ES6 promise for loop

이 글에서 보고 내가 과제에 적용했던 부분, 그리고 이해한 내용을 정리하려고 한다.

for문안에서 Promise, then을 구현하고 싶었다.

먼저 코드로 내가 과제에서 구현하고 싶었던 부분은 아래와 같다.

for (let i = 0; i < 10; i++) {
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(i);
        }, 1000);          // 1초마다 resolve값으로 i가 넘어가서,
    })
    .then((data) => {
    	console.log(data); // 0,1,2.. 이런 순서대로 나오겠지? 
    });
}
  • 반복문 안에 Promise객체를 생성했다.
    내 의도는 0 ~ 9까지 1초마다 순차적으로 나오는 것이었다.

  • 그러나 console창에서 실행하면, 1초 뒤 0 ~ 9까지 모두 실행된다.
    그 이유는 동기적으로 모든 Promise객체가 생성되었기 때문이다.

  • for문이 실행되면 초기값 i는 0부터 시작해서, 1씩 증가하면서 총 9번 실행된다.
    실행될 때마다 {} 안의 명령문인 new Promise가 실행되어 Promise 객체가 생성된다.

  • 총 9개의 Promise객체가 생성되면서 "1초 뒤 resolve를 실행하라"는 명령문이
    setTimeout함수를 통해 예약된다.
    그러나 동기적인 작업인 for문이 끝나기 전까지는 Promise는 실행되지 않는다.

  • 즉, for문이 전부 종료되기 전까지 Promise객체pending(대기) 상태로 있다가,
    for문이 종료되면 1초 뒤 resolve 함수를 실행해서 then으로 i값전달한다.
    따라서 1초 뒤 동시에 console.log(i)가 실행되는 것이다.

위 코드를 살짝 바꿔 실행해보자.

for (let i = 0; i < 10; i++) {
    //pending변수에 promise를 담았다.
    const pending = new Promise((resolve, reject) => { 
        setTimeout(() => {
            resolve(i);
        }, 1000);      
    })
    .then((data) => {
    	console.log(data);
    });
    console.log("pending", pending); //pending값을 확인해보자.
}

peding 변수에 pending(대기)상태의 Promise객체가 담겨 있다.
for문이 종료되면 1초 뒤 resolve함수는 then으로 i값을 전달해서, 0 ~ 9의 값이 보여진다.

해결해보자.

// 초기값에 i 뿐만 아니라 pending 변수도 함께 작성한다
for (let i = 0, pending = Promise.resolve(); i < 10; i++) { // --- ①
    pending // --- ⑤
        = pending // --- ⑥ 
            .then(() => { // --- ②
                return new Promise((resolve) => {
                    setTimeout(() => {
                      resolve(i); // --- ③ 
                    }, 1000);
                });
            })
            .then((data) => { // ④
                console.log(data);
            });
    console.log("pending", pending); //pending 값도 체크해보자.
}

for문이 시작되면,

  • ⓵ i = 0이고, pending변수에는 Promise.resolve가 실행되어 Promise.then 객체가 담긴다.
  • ⓶ pending변수에 담긴 Promise.then객체는 thenable하기 때문에 then을 실행할 수 있다.
  • ⓷ setTimeout 함수가 1초 뒤 resolve함수를 실행한다.
  • ⓸ resolve함수에 담겨 있던 i값이 then으로 전달되어 실행된다.
  • ⓹ 4단계 then 실행으로 생성된 Promise객체가 pending 변수에 다시 담긴다.
    ( i가 0인 첫번째 loop 가 끝난다. )
  • ⓺ i = 1 loop가 시작되면, 첫번째 루프에서 담긴 promise 객체로 then chain이 시작된다.

위과 같은 과정으로 loop가 반복된다.

즉, loop가 반복되면서 promise객체가 생성될 때 pending 상태이지만,
then chain이 형성되는건 loop의 반복 순서 대로 생성되어 있기 때문에
loop동기적으로 실행 되는 것처럼 보여진다.

다른 방법은?

내가 과제를 만들 때 사용했던 for문 + Promise 말고
async await을 쓰면 충분히 가능할 것이다.
(블로그를 쓰다가 발견한 자료인데, 이 자료를 참고하면 Promise.all로도 될 것 같다.
Promises inside a loop - Javascript ES6 )

그러나 Promise로 구현한 덕분에 Promise를 조금이나마 이해할 수 있었다.
지금도 사실 잘 모르는 것 같다.....
ken님 말대로 Promise로 장난질(?)은 더 해봐야 될 것 같다.

참고

profile
꾸준히, 끄적끄적 해볼게요 :)

0개의 댓글