2oneweek.dev

28. Promise의 매커니즘

    Tags

  • JavaScript
28. Promise의 매커니즘 thumbnail

Promise 매커니즘

( 1 )new Promise(function(){})

  • new 연산자로 Promise 를 호출하면, Promise.prototype.constructor 를 호출하여 Promise 객체를 만든다.
  • 일반적으로 new Point(one,two) 처럼 인스턴스의 초깃값을 생성자에게 넘겨주지만, Promise 는 비동기 처리를 위한 환경을 만들어 주는 것을 목적으로 고안되어, 파라미터에 함수를 넘겨준다.
  • 생성자에게 실행자를 파라미터로 넘겨주는 것은 확장성을 고려한 것이다. 실행자 함수는 생성자(constructor)가 확장된 개념으로, JS에서 정해진 생성자의 로직을 따르는게 아니라, 개발자가 정한 실행자의 로직으로 객체를 생성할 수 있게 해준다.



( 2 ) 실행자의 파라미터에 함수 "이름"을 넘긴다.

  • Promise에 인자로 들어가는 실행자는 함수 이름을 파라미터로 받는다.
    • new Promise((resolve, reject) => { })에서 (resolve, reject) => {}가 실행자이며, 인자로 resolve와 reject라는 함수 이름을 파라미터로 받는다.
  • 엔진은 실행자의 파라미터를 식별자로 하는 function Object를 생성한다. 즉, resolve와 reject라는 function Object를 생성한다. new Promise((resolve, reject) => { resolve("성공"); reject("실패"); });
    • 실행자의 파라미터가 Function Object이므로 호출이 가능하다.
    • 우리가 따로 resolve와 reject에 대한 함수를 정의하지 않았어도, 오류가 나지 않는 것은 엔진이 실행자의 파라미터를 보고 function Object를 만들었기 때문이다.
  • resolve와 reject는 엔진이 만든 함수다. 따라서, resolve와 reject는 호출되면, 파라미터를 then에 맵핑하는 것 이외에, 엔진이 지정한 부가적인 처리를 할 수 있게 되었다.
  • 주의할 점은, 실행자의 파라미터로 들어가는 함수를 익명함수로 하면, 에러가 발생하진 않지만, then()이 호출될 수 없다.



( 3 ) 파라미터 순서에 의한 성공/실패 함수 구분

var obj = new Promise((one, two) => { one(); }); obj.then( (value) => { console.log(value); }, (reason) => { console.log(reason); } );
  • 엔진은 Promise가 성공/실패 했을 때, 호출할 함수를 실행자 파라미터 순서로 구분한다.
  • Promise 상태가 fullfiled면, 실행자의 첫 번째 파라미터를 호출한다.
  • Promise 상태가 rejected면, 실행자의 두 번째 파라미터를 호출한다.
    • 순서로 구분하기 때문에, 실행자의 성공, 실패 여부 함수의 이름을 임의로 작성할 수 있다. 그러나 성공함수의 이름은 resolve로, 실패 함수의 이름은 reject로 하는 것이 시멘틱이다.
  • 엔진이 만든 성공/실패(resolve/reject) function Object는 추후, then 호출시에 사용되기 위해 어딘가에 저장해둬야 한다. 엔진은 성공/실패 function Object를 Promise 인스턴스의 내부 프로퍼티 [[PromiseStatus]]에 저장해둔다.



( 4 ) Promise 상태 결정의 순간

지금껏, Promise 상태가 fullfilled나 rejected로 변하면, 그때 성공/실패 함수를 호출한다고 정리했다. 그러나 이것은 사실이 아니다. 인과관계가 잘못되었다.

사실은, 실행자에서 resolve가 먼저 호출되면 Promise의 상태가 fullfilled가 되고, reject가 먼저 호출되면 rejected상태가 되는 것이다. 즉, resolve/reject에 의해 Promise의 상태가 결정되는 것이다.

new Promise((resolve, reject) => { reject("실패"); resolve("성공"); }).then( (value) => console.log(value), (reason) => console.log(reason) ); //=> 실패 new Promise((resolve, reject) => { resolve("성공"); reject("실패"); }).then( (value) => console.log(value), (reason) => console.log(reason) ); //=> 성공
  • resolve를 먼저 호출하면, Promise 인스턴스의 내부 프로퍼티 [[PromiseStatus]]는 fullfilled가 된다.
  • reject를 먼저 호출하면, [[PromiseStatus]]는 rejected가 된다.

따라서, 성공과 실패 여부는 실행자 안의 로직에서 결정해야 된다는 것이다. 분기문으로 Promise가 성공했다고 판단되었을 때 resolve를 호출하고, Promise가 실패했다고 판단되는 경우엔 reject를 호출하는 로직을 직접 작성해야 된다.

Promise는 개발자가 비동기 처리를 원하는 코드를 작성할 때 쓰는 것이다. 엔진이 알아서 해주는 것은 function Object 생성과, 개발자가 정한로직에 의해 성공/실패 상태를 결정하고 성공/실패 함수를 호출해주는 것 뿐이다.




( 5 ) 인스턴스에 저장과 사용

  • then()의 핸들러 함수에서 사용할 값들을 Promise 인스턴스에 저장한다.
new Promise((resolve, reject) => { resolve("성공"); }).then( (value) => console.log(value), (reason) => console.log(reason) );
  • resolve가 호출되면 "성공"을 Promise의 [[PromiseValue]]에 저장한다.
  • then()이 실행될 때, 엔진은 먼저 [[PromiseStatus]] 를 살펴본다.
    • 값이 fullfilled면 then()의 첫 번째 파라미터(핸들러 함수)를 호출한다.
    • 값이 rejected면 then()의 두 번째 파라미터(핸들러 함수)를 호출한다.
  • 핸들러 함수가 호출되면서 동시에 [[PromiseValue]]의 값과 핸들러의 파라미터를 맵핑한다.



( 6 ) Promise 객체의 반환

new Promise((resolve) => { resolve(100); }) .then((value) => { console.log(value); return value + value; }) .then((value) => { console.log(value); });
  • then()catch()는 Promise 객체를 받아, Promise 객체를 반환한다.
    • 따라서 then()catch()로 체이닝이 가능한 것이다.
  • then()은 비동기로 실행되나, 이어지는 처리는 동기/비동기 모두 가능하다. 현재의 then에서 비동기처리를 안해주면 이어지는 then은 동기적으로 실행된다.
    • return new Promise를 하게되면 비동기적으로 이어지는 then이 실행된다.
Written by@2-one-week
현재 블로그 개발 중

GitHubLinkedIn