JavaScript 세계에서는 거의 대부분의 작업들이 비동기(asynchronous)로 이뤄진다. 어떤 작업을 요청하면서 콜백 함수를 등록하면 작업이 수행되고 나서 결과를 나중에 콜백 함수를 통해 알려주는 식이다.
Synchronous(동기) 방식은 한가지 요청이 완료되고 나면 다음 요청을 시행한다.
Asynchronous(비동기) 방식은 응답에 관계없이 바로 다음 요청을 시행한다. 통신이 필요한 곳에 많이 이용된다.
Asynchronous JavaScript and XML
비동기적인 웹 애플리케이션의 제작을 위해 아래와 같은 조합을 이용하는 웹 개발 기법이다.
JavaScript 자체는 동기적(synchronous), blocking, single-threaded 언어이다.
이러한 JavaScript가 비동기적으로 동작하도록 하는 몇 가지 방법이 있다. 콜백함수 이용하기, Promise, 그리고 async-await을 이용하는 것이다. 그 중에서 Promise를 먼저 알아보겠다.
유명한 가수가 앨범을 언제 내는지에 대한 소식을 원하는 팬들을 위해 구독리스트를 만들었다. 그리곤 그 리스트를 팬들에게 전달해 이메일 주소를 적게 하고, 앨범이 준비되면 리스트에 있는 팬들에게 메일을 보내 앨범 관련 소식을 바로 받아볼 수 있게 했다.
이 비유는 코딩을 하면서 아래 같은 상황과 유사하다.
promise 객체는 아래와 같은 문법으로 만들 수 있다.
let promise = new Promise(function(resolve, reject) {
// executor (제작 코드, '가수')
});
executor의 parameter인 resolve와 reject는 자바스크립트에서 자체 제공하는 콜백이다. 개발자는 resolve와 reject를 신경 쓰지 않고 executor 안 코드만 작성하면 된다. 이때, executor는 자동으로 실행된다.
대신 executor에선 결과를 즉시 얻든 늦게 얻든 상관없이 상황에 따라 parameter로 넘겨준 콜백 중 하나를 반드시 호출해야 한다.
한편, promise 객체는 state(pending/fulfilled/rejected)와 result(undefined/done/error) 같은 '내부 property(개발자가 직접 접근할 수 없다.)'를 가진다.
간단하게 일정 시간 후에 resolve와 reject 콜백을 호출하는 executor를 가진 promise를 만들었다.
let promise = new Promise(function(resolve, reject) {
// 프라미스가 만들어지면 executor 함수는 자동으로 실행됩니다.
// 1초 뒤에 일이 성공적으로 끝났다는 신호가 전달되면서 result는 'done'이 됩니다.
setTimeout(() => resolve("done"), 1000);
});
let promise = new Promise(function(resolve, reject) {
// 1초 뒤에 에러와 함께 실행이 종료되었다는 신호를 보냅니다.
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
프라미스 객체는 executor(‘제작 코드’ 혹은 ‘가수’)와 결과나 에러를 받을 소비 함수(‘팬’)를 이어주는 역할을 한다. 소비함수는 .then, .catch, .finally 메서드를 사용해 등록(구독)된다. 소비함수들을 통해 내부 property에 접근이 가능하다!!
promise.then(
function(result) { /* 결과(result)를 다룹니다 */ },
function(error) { /* 에러(error)를 다룹니다 */ }
);
첫번째 인수는 promise가 이행되었을때 실행되는 함수고, 여기서 실행결과를 받는다. 두번째 인수는 promise가 거부되었을때 실행되는 함수고, 여기서 에러를 받는다.
만약 작업이 성공적으로 처리된 경우만 다루고 싶다면 .then에 인수를 하나만 전달하면 된다.
.catch(fn)는 문법이 간결하다는 점만 빼고 .then(null,fn)과 완벽하게 같다.
프라미스가 처리되면(이행이나 거부) f가 항상 실행된다는 점에서 .finally(f) 호출은 .then(f, f)과 유사하다. 쓸모가 없어진 로딩 인디케이터(loading indicator)를 멈추는 경우같이, 결과가 어떻든 마무리가 필요하면 finally가 유용하다.
new Promise((resolve, reject) => {
/* 시간이 걸리는 어떤 일을 수행하고, 그 후 resolve, reject를 호출함 */
})
// 성공·실패 여부와 상관없이 프라미스가 처리되면 실행됨
.finally(() => 로딩 인디케이터 중지)
.then(result => result와 err 보여줌 => error 보여줌)
위와 같이 사용한다.
그런데, finally는 then(f, f)과 완전히 같진 않다. 차이점은 다음과 같다.
promise를 이용하면 콜백지옥을 피할 수 있게 된다. promise chaining과 async-await 등은 다음에 한 번 다뤄보도록 하겠다.
Is JavaScript Synchronous or Asynchronous?
Asynchronous JavaScript
Promise-basic