callback / Promise / async | await

길고 꾸준하게·2022년 5월 17일
2
post-thumbnail

callback

콜백.. 정말 많이쓴는 패턴이다,
각종 패키지들의 메서드의 인자로 화살표함수를 넣거나 handle함수를 따로 만들어서 넣어주거나.. 하는 모든게 사실 callback이다

JavaScript의 비동기를 다루는 방식때문에 (이벤트루프 & 태스크큐 & WebAPI) 코드의 흐름대로 코드가 실행되지 않는다는것을 나는안다.
그래서 콜백의 사용은 내 나름대로의 정의로는.

함수의 순서를 비동기 처리가 끝난시점에 호출하도록 정해주고싶을때 사용한다.

라고 정의하겠다.

인자를 뚫어서 콜백함수로 넘겨주고. 해당 시점에 호출한다. 하지만 처리하고싶은 로직이 많으면 함수안에서 함수가 실행되고 또 그안에서 함수가 실행되고..
코드 가독성 측면에서도. 에러를 핸들링 하는 측면에서도 어지러운 수준이된다.

Promise

비동기처리를 콜백으로만 하기엔 너무 어지럽기때문에 ES6에서 처음나온 신문법이다.

Promise는 비동기 작업의 최종 완료 / 실패를 나타내는 object(객체)다.

- 3가지 상태

Promise는 3가지 상태를 가진다.

  • pending (대기) : 초기상태. 비동기 처리를 수행하지도 / 거부하지도 않음
  • fulfilled (수행) : 비동기 처리가 성공적으로 완료됨.
  • rejected (거부) : 비동기 처리가 실패함.

- 생성

const promise = new Promise((resolve,reject)=>{
	//비동기 작업
})

promise생성자는 특별한 함수인자 하나를 받는데 executor라는 이름으로 불리며.
executor함수는 첫번째인자로 resolve(성공) , reject(실패)를 받는다.
그리고 우리가 아는 promise를 리턴해준다.

하지만 Promise 자체가 최종결과를 반환하는것은 아니다. Promise의 then 메서드에 의해 최종 결과가 반환될 것이다.

- case

const promise = new Promise((resolve,reject)=>{
	//비동기작업
    resolve('success') -case1
    reject('err!') - case2
})
promise.then(val => console.log(val)) // success
promise.catch(err => console.log(err)) // err!

성공인 resolve가 호출되면 .then에 있는 동작들만 실행되고.
실패인 reject가 호출되면 .catch에 있는 동작들만 실행된다.

- then,catch

then

then은 promise메서드이며, promise를 리턴하고,
2개의 콜백함수를 인자로 받는다.
각각은 promise가 이행(fulfilled) / 거부(rejected)를 위한 콜백함수다.

몰랐던부분. then이 콜백함수를 인자로 받는건 알았지만 2번째의 인자가 실패할 경우의 콜백함수도 받는다는것을 몰랐다. 여태까지 성공했을때의 경우만 다루는건줄 알았다.

promise.then(onFulfiled,onRejected);

promise.then((val)=>{ //이행 }, (err)=>{ //거부 })

catch

마찬가지로 promise 메서드이자 거부(rejected)된 사례만 반환하고 처리한다.

then이 2번째인자로 거부를 위한 콜백함수를 받는다했다.
결국 catch는 then(null,onRejected)의 축약형이다. => mdn참조

Promise의 장점

- Chaining

두 개 이상의 비동기 처리를 해야할때. Callback이라면 함수안에 함수.. 함수안에 함수.. 콜백지옥을 볼것이다.
하지만 Promise 콜백함수를 then을 이용해 옆으로 쌓는다. 그렇기때문에 코드 가독성 측면도 우수하다.

// callback

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

-----

// promise - chaining

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

then이 promise를 반환하기 때문에 promise메서드인 then으로 또 이어나갈수 있는거다.

async / await

async

async function은 비동기함수를 정의한다. 비동기함수는 일반함수처럼 콜스택에 쌓이는게 아닌 이벤트루프를 통해 비동기적으로 작동하는 함수다.

내나름대로의 정의다. 그리고 중요한건 async는 promise를 리턴한다.

async function test(){}

console.log(test()) // Promise{fulfilled:undefined}

async function test2(){return:'abc'}

console.log(test2())
Promise {<fulfilled>: 'a'}

진짠가 해서 실험해봤다.. 진짜 저렇게 나온다. 결국 promise를 리턴하기때문에 promise메서드인 then catch를 사용해서 성공/실패 로직을 다룰수 있을것이다.
promise와 차이점은.

  • promise의 특별한 함수인자인 executor함수의 본문만 남긴다.
  • resolve(value)는 비동기함수(async function)의 return값이다.
  • reject(err)는 비동기함수의 throw new Error(err)로 처리된다.
async function test(arg){
    if(arg) return `resolve case`
    throw new Error('reject case')
}

test(true).then(val => console.log(val)) // 'resolve case'
test(false).catch(err => console.log(err)) // 'Error : reject case'

하지만 async의 특장점은 await을 사용할수 있다는 것일거다.

await

async function 내부에서 promise를 '기다리기 위해' 사용된다.

기다린다? 비동기처리는 보통 이벤트루프를 통해 태스크큐에서 대기후 콜스택이 비어있어야 즉. 다른 동기작업이 다 끝난 후에 태스크큐에서 빠져나와 콜스택으로 간다.

기다린다는건 즉

promise가 fulfill(성공) 되거나 reject(거절) 될때까지 async함수의 실행을 일시정지하고. promise가 fulfill(성공) 되면 정지한 시점부터 다시 함수의 실행을 이어나간다.

즉 async function(비동기함수) 내부에 promise가 성공/실패든 결과가 나올때까지 await이하에 적인 함수의 실행은 멈추고.
결과가 나오면 다시실행하며. await 다음에 오는 것이
promise일 경우에는 fulfill시 반환하는 값이,
promise가 아닐경우에는 resolve'된' promise가 올거다 => Promise.resolve(value)

async function test(){
	const a = await 1
    console.log(a) // 1
}
//
const a = Promsie.resolve('test')
a.then(val => console.log(val)). // 'test'

오류처리

await은 fulfill된 값이 들어온다. 그럼 promise가 reject상태이면 어떨까?

// case1 -> try/catch
async function test(){
	try{
    	const reject = await Promise.reject('err')
    } catch(e){
    	console.log(e) // 'err'
    }
}

// catch -> promise method
async function test(){
	const reject = await Promise.reject('err')
    console.log(reject)
}

test() // Promise {rejeted : 'err'} //async함수 자체가 promise를 리턴한다.

test().catch(err => console.log(err)) // 'err'

1번째는 try / catch로 오류처리를 했고 await에 reject된 promise가 들어왔다면
reject된 값이 들어 throw된다. 그래서 바로 reject값인 'err'가 나온모습

2번째는 promise method인 catch를 이용한 부분.
async function이 promise를 리턴한다고 했으니 catch method를 이용해 reject 값을 throw한 모습

...근데 이해가 안가는점이 있다. 2번째 케이스에서 reject를 콘솔로 찍으면 안나온다.. 테스트 결과 reject전단계 에서 콘솔을 찍으면 나오는데 Promise.reject()가 나온 다음 코드블럭은 진행이안된다.

Promise.reject() resolve()가 뭔지 더 추후에 봐야겠다.

길기도하다..

출처

https://elvanov.com/2597
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise

mdn이 최고시다

profile
작은 나의 개발 일기장

0개의 댓글