핑클을 아는 친구들이 있을까? 어렸을적 옥주현의 약속해줘~~~ 를 보고 "그래 옥주현은 가창력으로 아이돌하는거지~" 하고 생각했던 어린 시절이 있었다.
promise에 대해 생각하면 이 짤이 생각나곤 했는데 뭐 전혀 관련 없지만...ㅋㅋㅋㅋ
여튼 이번에는 promise를 알아보겠다!!
Promise는 프로미스가 생성된 시점에는 알려지지 않았을 수도 있는 값을 위한 대리자로, 비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 처리기를 연결할 수 있습니다.
JS는 동기적인 처리만 할 수 있는 single Thread 방식의 언어이다.
즉, 비동기처리는 할 수 없다는 이야기인데
Promise는 비동기 연산을 처리 후 결과값을 resolve(성공)과 reject(실패) 경우로 나눠 처리할 수 있게 해준다.
-> Promise로 비동기 처리 후 then이나 catch로 성공 경우와 실패 경우의 결과값을 나눠 다룰 수 있고, 또 새로운 promise로 연결할 수도 있다.
비동기에서 동기 메서드처럼 값을 반환할 수도 있는데
단, 최종 결과가 아닌 어떤 시점에 결과를 제공하겠다는 Promise를 반환한다.
대기(pending): 이행하지도, 거부하지도 않은 초기 상태.
이행(fulfilled): 연산이 성공적으로 완료됨.
거부(rejected): 연산이 실패함.
function doAsyncTask() {
const promise = new Promise((resolve, reject)=> {
console.log("Async work")
if(true){
//성공경우 ()안에 값을 넣어서 보내도 된다.
resolve({x : 1})
}else{
//실패경우 ()안에 값을 넣어서 보내도 된다.
reject("Error")
}
}
)
return promise;
}
doAsyncTask()
.then(()=> console.log("Task Compelete"))
.catch((error) => console.log(error)
위 코드처럼 promise를 써서 resolve에 성공한 경우 reject에 실패한 경우를 보내주면 되는데 reject
만약 실패한 경우 실패한 이유를 error값으로 받고 싶다면 위 처럼
catch(error)로 error 값을 받아도 된다.
catch 대신 여기서 then을 또 쓰고 그안에 error를 써도 error를 다룰 수 있다.
.then(()=>console.log("할일을 하고 ")) .then((error) => console.log("에러처리를 then으로", error)
promise는 promise를 반환하기 때문에
이미 resolve된 값이라도 .then 체인을 쓸 수 있다.
const promise = Promise.resolve("done")
promise.then((value)=>console.log("resolve된 이후라도 ", value))
--> 콘솔창에는 "resolve된 이후라도 done" 이 찍힐 것이다.
또는 이런 것도 된다.
function doAsync(){
return Promise.resolve()
}
doAsync().then( () => console.log(message))
let message = "promise resloved"
이와 같은 경우 동기처리식이였으면 콘솔에는 message를 알 수 없다는 error가 떴을 것이다. 하지만
비동기 처리를 했기 때문에 다음 식은 문제 없이 message의 값을 출력할 것이다.
이점을 잘 기억해둬야 한다.
Promise가 resolve 했던 reject 했던 finally 안에 행동을 실행한다.
Promise.resolve("done")
.then(val => {
throw "fail";
})
.catch(err => console.log(err))
.finally(()=> console.log("Cleaning UP"))
위 코드는 어떤 값을 콘솔 창에 출력할까?
프로미스가 done을 resolve하고나면 "fail"이라고 에러를 띄운다
그러므로 catch에서 error를 인식해 콘솔창에 error를 띄울텐데 그리고 끝일까?
보통의 경우 에러가 발생하면 error를 띄우고 끝난다. 하지만 이 경우 finally가 있기 때문에 에러 메세지 후에 finally의 "Cleaning UP"도 나오는 것을 확인할 수 있다. --> 어떤 경우에도 finally는 실행된다.
비동기처리시 데이터 fetching과 같은 작업은 굳이 작업별 순서를 정하지 않아도 된다. 외부 API에서 데이터를 가져오는 작업들이 각자 걸리는 시간이 다르므로 동시에 진행해도 무방한데 이를 가능하게 해주는게 Promise.all이다.
const doAsyncTask = (delay) => {
return new Promise(resolve => setTimeout(() => resolve(delay),delay))
let promises = [doAsyncTask(1000), doAsyncTask(2000), doAsyncTask(500)]
Promise.all(promises).then(values => console.log(values))
위 코드 실행시 콘솔 창에는 몇초후에 값이 찍힐까?
만약 비동기 작업이 순차적으로 진행되었으면 총 3.5초 뒤에 값이 나왔을 것이다.
하지만 3가지 비동기 작업을 동시에 진행하게 all을 썼기 때문에 2초뒤에 값이 나오게 된다.
차이 설명을 위한 코드부터 보자
const doAsyncTask = () => {
Promise.resolve("done")
// IIFE 로 async await 사용
(async function(){
let value = await doAsyncTask()
console.log(value)
console.log(done2)
})()
위 코드를 실행하면 어떻게 출력이 될까?
async는 함수를 비동기처리 함수로 만들어주고 await 는 비동기처리하는 값이 올때까지 기다려주는 역할을 한다.
그렇기 때문에 위 코드에서 doAsyncTask() 값을 기다려주고 밑에 있는 코드들이 실행이 되므로
done done2
가 출력되는 것을 알 수 있다.
만약에
(async function(){
doAsyncTask().then((value) => console.log(value))
console.log(done2)
})()
으로 promise then 체인이었다면 출력은
done2 done
이었을 것이다.
참고
Udemy Advanced Javascript -> Asim Hussain