비동기 시리즈: 약속

비동기 단순화, 약속합니다



콜백은 비동기 호출 순서를 관리하는 데 도움이 될 수 있습니다. 그러나 너무 많으면 상황이 복잡해집니다. 운 좋게도 확실하게 보여주는 대안이 있습니다... 약속이 있습니다.

지옥의 문



이 시리즈의 마지막 게시물에서 중첩 콜백을 사용하여 다음 솔루션에 도달했습니다.

//replace reference to doPrintGreenRed with an anonymous function
printBlue("Blue", function(){
   //calls printGreen with our desired parameter 
   printGreen("Green", function(){
     //calls print red with our desired parameter
     printRed("Red");
   });
});


그러나 더 많은 콜백을 호출해야 할수록 더 많은 콜백을 정의해야 합니다. 어느 시점에서 콜백 지옥이라는 현상을 경험하게 될 것입니다.



각 콜백에서 수행하는 것이 얼마나 지저분한지exception handling는 말할 것도 없습니다.

try{
 printBlue("Blue", function(){
   try{
     printGreen("Green", function(){
       try{
         printRed("Red");
       }catch(e){
         console.error(e);
       }
     });
   }catch(e){
     console.error(e);
   }
 });
}catch(e){
   console.error(e);
}


그래서 지금은?



2015년에 출시된 JavaScript 6번째 버전에서 Promise가 출시되었습니다. 콜백을 직접 수락하는 대신 비동기 함수는 이제 Promise 객체를 반환할 수 있습니다.

이러한 약속 개체는 비동기 함수의 주요 작업이 완료될 때 콜백을 받아 실행하는 then() 메서드를 제공합니다.

다행스럽게도 인쇄 기능은 Promise를 반환하므로 중첩된 콜백을 다음과 같이 다시 작성할 수 있습니다.

 printBlue("Blue")//moved our callback from here
  .then(function(){//to here
    printGreen("Green")
     .then(function(){
       printRed("Red");
     })  
  })


원하는 출력을 얻었습니다. 그러나 이것이 실제로 콜백 접근 방식보다 개선된 것입니까? 여전히 매우 비슷해 보입니다. then()에 대한 것은 또 다른 약속을 반환한다는 것입니다!

then()은 이전 약속이 해결되었다고 말한 후 다른 약속을 반환합니다.

then()을 반복적으로 호출하여 약속 체인이라는 것을 형성할 수 있습니다.

 printBlue("Blue")
  .then(function(){
    //only executes after printBlue() resolves
    printGreen("Green");// instead of calling then here
  })
  .then(function(){ // we call it here
    printRed("Red");//only executes after printGreen resolves
  })
  .catch(e){
    console.error(e);
  }  


이제 중첩이 평평해졌지만 여기서 주요 이점은 약속 객체에서 제공하는 catch() 메서드를 사용한다는 것입니다.

체인 끝의 캐치는 체인의 어느 부분에서 발생했을 수 있는 모든 오류를 처리합니다!

이는 가독성 및 오류 처리 측면에서 크게 개선되었습니다.

약속하기



고차 add()를 작성할 수 있는 것과 마찬가지로 약속을 반환하는 해당 함수의 버전도 작성할 수 있습니다. printRed/Green/Blue 함수와 달리 add()에 의해 반환된 약속은 값으로 해결됩니다. 그 값은 then() 메소드에 전달된 모든 함수에 의해 수신됩니다.

function add(a, b){
  //create a promise object
  const promise = new Promise(function(resolve, reject){
    if(typeof a !== "number" or typeof b !== "number")
      reject("Invalid parameter error");//how errors are thrown
    else
      resolve(a + b);//how values are returned when the work of the function is complete
   })
  return promise;//return our promise
}


약속 객체를 생성할 때 2개의 콜백을 제공해야 합니다. resolve() 및 거부().

값을 반환하기 위해 return을 사용하는 대신 resolve() 함수를 사용합니다. resolve()에 전달된 모든 것은 then()에 제공된 모든 콜백에 전달됩니다.

오류를 발생시키기 위해 throw를 사용하는 대신 reject() 함수를 사용합니다. reject()에 전달된 모든 것은 catch()에 제공된 모든 콜백에 전달됩니다.

add(5,10)
  .then(function(ans){
    console.log(ans);//logs 15
    return ans;//passes this value to next then in the chain
  })
  .then(function(ans){
    return add(ans, 5);//returns a new promise to the next then
  })
  .then(function(ans){
    console.log(finalAns);//logs 20
  });

add(11, 'cat')
  .then(function(ans){
    console.log(ans);
    //this is not executed because of the type check in the add()
  })
  .catch(function(error){
   console.error(error);//logs 'Invalid parameter error'
  });


then()에 전달된 콜백에서 반환된 모든 값은 체인에서 다음 then()의 콜백으로 전달됩니다. 이렇게 해서 2번째 then() 콜백이 1번째 then() 콜백의 결과를 받을 수 있었습니다.

약속.모두()



지금까지 살펴본 예제에서는 비동기 호출의 순서가 중요하므로 흐름 제어를 수행하는 데 then을 사용했습니다. 비동기 호출이 독립적이지만 각 호출의 결과를 결합해야 하는 경우 Promise.all()을 사용할 수 있습니다.

Promise.all()은 여러 약속을 비동기식으로 실행하지만 최종 값을 배열로 수집합니다.


let promise1 = add(5, 10);
let promise2 = add(11, 12);
let promise3 = add(7, 8);

//Async execution (faster)
Promise.all([promise1, promise2, promise3])
  .then(function(result){
    console.log(result);// logs [15, 23, 15]
  })


우리의 추가는 서로 독립적이기 때문에 추가를 동 기적으로 수행하기 위해 then()을 사용하지 않습니다. 대신 비동기로 유지됩니다. 이것은 실제로 동기화하는 것보다 더 빠르게 실행됩니다.

중요: 순서가 중요하거나 호출이 서로 종속된 경우에만 then()과 호출을 동기화합니다.

//Sync execution (slower), not needed in this case 
//also relies on global state arr

let arr = [];

add(10, 5)
  .then(function(sum1){
    arr.push(sum1);
    return add(11, 12);
  })
  .then(function(sum2){
    arr.push(sum2);
    return add(3, 4)
  })
  .then(function(sum3){
    arr.push(sum3);
    console.log(arr);
    //logs [15, 23 7] the result of all promises that resolved in the chain
    //this result is only available in this scope
  });

console.log(arr);
//logs [] empty array because this log runs asynchronously with the first call to add(). 
//The other promises have not resolved yet.



결론



이 게시물에서 Promise가 중첩된 콜백을 함께 연결하여 개선되는 방법을 보여주었습니다. 그러나 모든 호출의 결과는 체인의 끝에서만 사용할 수 있다는 한계가 있습니다.

항상 그렇듯이 여기REPL에서 이 문서의 코드를 시도할 수 있습니다.

어쨌든 우리가 이것을 개선할 수 있습니까? 당신이 곁에 있으면 이 시리즈의 마지막 게시물에서 말할 것을 약속합니다.

좋은 웹페이지 즐겨찾기