-
[JavaScript] 비동기와 PromiseJavaScript 2022. 2. 13. 12:58728x90
비동기라는 단어 자체가 생소할 수도 있고, 몇 번 들어본 경우도 있을테지만 정확히 어떤 개념이고, 동작 흐름에 대해서설명해보라 한다면 쉽지 않았다. 이번 계기로, 비동기 처리에 대해 확실히 짚고 넘어가려 한다.
먼저 비동기를 살펴보기 이전에 동기적인 방식은 무엇을 의미하는지 알아보겠다.
동기(synchronous)
하나의 명령어가 끝날때까지 다음 명령어가 진행되지 않다가 끝나면 순차적으로 실행되는 방식을 말한다. 아래의 코드를 보면 이해하기에 수월할 것이다.
console.log(2); console.log(3); console.log(5); console.log(7);
콘솔창의 출력 결과는 2, 3, 5, 7 순서대로 출력되고 명령의 순서는 파악하기 쉽지만 작업이 끝나면 다음 작업이 수행되기에 실행 속도는 상대적으로 느릴 수 있다. 그렇다면, 비동기 처리는 어떠한 동작이 이뤄지는지 살펴보자.
비동기(asynchronous)
일반적인 실행 순서와 달리 독립적으로 동작하여 프로그램이 병렬적인 실행 방식을 나타낸다. 이 또한 코드를 살펴보겠다.
console.log(2); console.log(3); setTimeout(function() { console.log(5) }, 5000); console.log(7);
본 코드의 3번째 라인을 보면 setTimeout 함수 내부에 함수와 초를 나타내는 5000ms가 인자로 들어가 실행되는데, 이처럼 바로 실행되진 않지만 다른 함수의 입력값으로 전달되어 다른 함수에 의해 나중에 호출되는 함수를 콜백함수라고 한다.
따라서, 실행결과를 확인해보면 2, 3이 출력된 후 setTimeout은 독립적으로 동작해 마지막 7 다음 5초 후에 5가 출력되는 것이라고 볼 수 있다. 혼란스러울 수 있으나 실행 속도는 비교적 훨씬 빠를 것이다. 아래의 그림을 참고하면 한번에 이해할 수 있을 것이다.
콜백함수의 이해도를 높이기 위해서 문자열 배열에 있는 데이터 중 크기가 5보다 큰 문자열들을 반환해주는 코드를 확인해보자.
words = ['asynchronous', 'fetch', 'limit', 'api', 'javascript']; newWords = words.filter((word) => { return word.length > 5; }); console.log(newWords); // ['asynchronous', 'javascript'];
배열 객체의 메소드인 filter의 인자로 callback이 들어가는데 이 자리에 화살표 함수가 들어가 문자열 크기에 따라 true or false를 반환하고 true인 문자열들이 배열로 출력되는 것을 확인할 수 있다.
이처럼 콜백함수와 함께 어떠한 명령이 실행될 때 언제 실행이 될지 예측하기 어렵다거나 주가 되는 작업이 아닌 경우 비동기적인 처리를 많이 한다.
Promise
위와 같은 설명들을 한 이유는 모두 Promise에 대해 정확히 이해하기 위해서이다. Promise는 자바스크립트에서 비동기 처리를 할 때 사용되는 객체로써, 비동기적으로 동작해야 하는 함수들이 이 객체를 이용해 제어하는 경우가 많다고 한다.
대표적으로 웹 브라우저와 웹 서버 간 통신을 할 때 비동기 처리로 사용되는 fetch API가 Promise를 사용한다. fetch로 필요시 서버에 요청을 보내고 응답 정보를 받아올 수 있다. fetch에 대해 구체적으로 알고싶다면 다음의 링크를 참고하면 도움이 될 것이다.
https://developer.mozilla.org/en-US/docs/Web/API/fetch
fetch() - Web APIs | MDN
The global fetch() method starts the process of fetching a resource from the network, returning a promise which is fulfilled once the response is available.
developer.mozilla.org
fetch는 then, catch를 이용해 콜백함수로 비동기 처리를 하게되는데 이때 fetch의 반환값이 Promise이다. 이를 통해서 확인할 수 있는 사실은 해당 코드가 비동기적으로 동작하여 웹 브라우저에 결과를 출력하겠구나 라는 걸 파악할 수 있어야 한다. 이에 대한 코드는 다음과 같다.
fetch('https://jsonplaceholder.typicode.com/posts') .then(function(response) { // response.json.then(function(data) { // console.log('data', data); // }); return response.json(); }) .catch(function(reason) { console.log(reason); }) .then(function(data) { console.log('data', data); });
fetch로 실행된 결과가 성공적인 경우, then으로 전달한 콜백함수가 호출되면서 Promise 타입의 response 객체가 응답 결과의 parameter로 들어가 반환하게 되고, 정상적으로 수행되지 않는 경우 catch로 전달된 콜백함수를 호출하며 실패한 원인을 알려주는 객체가 parameter로 들어가 출력을 해주고 있다. 또한 chaning으로 인해 fetch는 처음 한 번만 붙여주면 된다.
response 객체에 있는 json으로 변환해줌으로써 자바스크립트의 데이터 형태라는 것을 알려주고, json이 반환하는 값 자체도 Promise 이다. 그렇기 때문에 이 Promise도 response.json() 변환 작업이 끝났을 때 then을 호출할 것이라는 예측할 수 있다.
콜백함수의 작업이 끝난 후 추가적인 작업을 수행하길 원한다면 chaning을 한다거나 then 내부에 중첩적으로 코드를 작성할 수 있다. (then을 중첩해서 쓰는 것 보다는 체이닝 방식을 더 권장하는 편이다.)
따라서, 자바스크립트 형식의 데이터(json)로 바뀌면서 통신을 할 때에 데이터 조작이 용이하게 되고 여러 작업들을 수월하게 진행할 수 있을 것이다.
다음은 위에서 살펴본 과정들을 특정 상태로 나타낸 것이고, Promise를 사용하는 이유를 알아보겠다.
[Promise의 상태]
- Pending : Promise를 사용했을 때의 진행 및 대기중인 상태
- Resolved : 결과가 성공적일 때 then으로 전달된 콜백함수가 호출되는 상태
- Rejected : 에러가 발생했을 때 catch로 전달된 콜백함수가 호출되는 상태
then, catch로 새로운 Promise를 반환하면 다시 Pending 상태가 된다.
fetch의 반환값이 Promise라 하였고 체이닝을 통해 then과 catch를 사용할 수 있었는데 사용자가 직접 Promise 타입의 객체를 만들어서 비동기 처리를 할 때 사용하고 싶다면 아래의 코드를 확인해보자.
function run() { return new Promise(function(resolve, reject){ setTimeout(function(){ resolve("run success"); }, 4000); }); } function work() { return new Promise(function(resolve, reject){ setTimeout(function(){ resolve("work success"); }, 4000); }); } run() .then(function(data) { console.log('data', data); // data run success return work(); }) .then(function(data) { console.log('data', data); // data work success })
이전과 동작방식은 비슷하며 Promise 객체만 직접 생성해서 반환해준 것 외에는 다를 게 없다.
단, 객체를 생성할 시점에서 콜백함수의 인자로 resolve, reject 두 개를 받는데 실행 결과가 성공을 할때만 resolve 를 호출하고 그렇지 않은 경우엔 reject를 호출한다는 점을 주의하여야 한다. 이후, work 함수에서도 반환값이 Promise 이므로 then으로 체이닝한 것을 확인할 수 있다.
왜 Promise를 사용하는가..?
- 비동기적인 작업을 처리할 때 그 작업의 성공 여부를 표준화된 방식으로 처리할 수 있게 해주기때문이다.
- 성공 또는 실패의 여부에 따라 표준화된 처리가 가능하다.
Promise라는 개념 자체가 생소할 수 있지만 사용되는 방식이나 비동기의 흐름을 익혀놓는다면, 클라이언트에서 요청한 정보들이 서버에서 어떻게 응답을 하고 그 결과를 이용해 또 어떤 작업을 할 수 있는지 알 수 있어 많은 도움이 되지않을까 생각해본다.
Promise에서 나아가 복잡성을 최소화 시키고 더욱 더 편리하게 비동기 작업을 해보길 원한다면 다음 포스팅을 참고하면 좋을 것 같다.
Promise에서 한 걸음 더, async & await
[JavaScript] Promise에서 한 걸음 더, async & await
Promise에서 만족하지 못하는 상황에서 비동기 처리를 하기 위해 더 편리하고 유용한 키워드인 async와 await에 대해 알아보겠다. Promise에 대한 이해가 부족하다면 아래의 포스팅을 참고하길 바란다.
6ro-29.tistory.com
728x90'JavaScript' 카테고리의 다른 글
[JavaScript] Promise에서 한 걸음 더, async & await (0) 2022.02.15 [JavaScript] 클로저(Closure)란? (0) 2022.02.10 [JavaScript] 편리함을 위한 ES6 (0) 2022.02.04 [JavaScript] ES6 Module (0) 2022.02.04