가장 중요한 전제조건
자바스크립트 엔진은 항상 동기식 처리를 진행한다 (Synchronous)
간단하게 말하자면 한번에 코드를 한줄씩 차례차례
실행한다는 소리이다.
출처 : https://sudo-minz.tistory.com/21
그러나 세상을 살다보면 반드시 동기적으로 처리할 수 없는 부분이 있다.
addEventListener, ajax request, 하다못해 setTimeout 도 이에 해당한다.
‘이 이벤트를 감지하기 전까지 기다려줘’ 라거나 ‘서버에서 요청을 다 받을때까지 기다려줘’ 같은
문제를 동기적으로 코드를 처리하는 자바스크립트 엔진 내부적으로는 해결할 수 없으며,
자바스크립트의 실행을 도와주는 웹 브라우저 API 로 해결한다.
출처 : https://baeharam.netlify.app/posts/javascript/event-loop
이런식의 코드 처리는 아래의 예시와 같은 문제를 야기할 수 있다.
console.log(1)
setTimeout(()=>{
console.log(2)
},0)
console.log(3)
// 1
// 3
// undefined
// 2
이 함수의 실행순서는 다음과 같다.
- console에 1을 출력한다.
- setTimeout 함수를 Web API에 등록한다.
- console에 3을 출력한다.
- queue에 모든 작업이 끝났으므로 Web API에 등록된 setTimeout 을 실행한다.
- setTimeout의 리턴값은 undefined
- console에 2를 출력한다.
이벤트 루프는 stack과 queue를 감시하다가
stack의 모든 처리가 끝나면 queue에 저장된 함수를 stack 으로 보내주는 역할을 한다.
이를 해결하기 위해 사용하는 방법은
가 있다.
콜백함수의 비동기처리에는 치명적인 단점이 두가지 있는데,
첫번째는 콜백 지옥에 빠진다는 것이고,
두번째는 에러 처리가 곤란하다는 것이다.
복잡한 처리과정이 있지만 요지는
에러처리를 해야하는 로직의 호출 스택에 비동기 처리의 함수가 존재하지 않기 때문이다.
이러한 문제를 극복하기 위해 ES6 부터는 Promise 라는 문법이 제안되었다.
알아두어야 할 점은,
promise 는 비동기처리를 위한 특정한 기능을 추가한 함수가 아닌
콜백함수를 조금 더 편하게 관리하기 위한 코드/함수의 디자인 패턴일 뿐이라는 점이다.
프로미스는 아래와 같이 사용할 수 있다.
let 약속 = new Promise();
약속.then(function(){
약속이 지켜졌을 때 실행할 코드
}).catch(function(){
약속이 지켜지지 않았을 때 실행할 코드
})
프로미스의 코드가 성공하면 then() 안의 함수를 실행해주세요.
실패하면 catch() 안의 함수를 실행해주세요.
라는 로직.
프로미스는 단지 함수의 성공/실패 판정을 하는 거짓말 탐지기 역할을 한다.
let 약속 = new Promise(function(resolve, reject){
성공했을 때 resolve()를 리턴하는 함수
실패했을 때 reject()를 리턴하는 함수
})
Promise 내부에 성공, 실패를 담당하는 파라미터를 넣어
성공했을 때 해당 첫번째 파라미터,
실패했을 때 두번째 파라미터를 리턴하도록 로직을 설계하면 프로미스를 사용할 수 있다.
프로미스 객체는 3가지의 상태를 가진다.
- pending(대기) : 아직 대기중인 약속
- Fulfilled(이행) : 약속을 모두 수행하고 Promise가 결과를 리턴. resolve() 를 실행할 때의 상태.
- Rejected(실패) : 약속을 수행했으나 실패함. 혹은 오류가 발생. reject() 를 실행할 때의 상태.
ES8 부터 사용할 수 있는 promise의 대체제.
async function 약속() {}
약속().then(function(){
console.log(resolve()
})
async 키워드는 항상 Promise 인스턴스를 반환하도록 설계되어 있다.
Promise 처럼 따로 new Promise 로 로직을 만들어주지 않아도
언제 어디에서나 키워드가 붙은 함수에 약속을 부여할 수 있는 기능을 가지고 있다.
await 는 thenable한 객체, promise 인스턴스를 읽는 역할을 한다. promise의 then 기능.
async 키워드의 경우 에러처리를 하는 키워드를 따로 명시해주어야 하는데,
방법은 다음과 같다.
async function f() {
try {
let response = await fetch('http://유효하지-않은-주소');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
에러처리 구문
try 로 resolve 의 로직을 받아오고,
실패했을 경우 catch 로 체크하는 로직을 구현하면 에러 핸들링을 할 수 있다.