자바스크립트 비동기

정은경·2020년 7월 6일
0

👸 Front-End Queen

목록 보기
6/278

1. 자바스크립트 비동기 처리

  • '비동기 처리'란 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트 특성을 의미
  • 자바스크립트에서 비동기 처리가 필요한 이유를 생각해보면, 화면에서 서버로 데이터를 요청했을 때 서버가 언제 그 요청에 대한 응답을 줄지도 모르는데 마냥 다른 코드를 실행 안하고 기다릴 순 없기 때문

비동기 처리 살례

  1. JQuery Ajax
  2. setTimeout()
  • Web API의 한 종류로 코드를 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행함

2. 콜백 함수

  • 콜백함수로 비동기 처리 방식의 문제점 (예:서버에서 데이터 응답이 아직 않왔는데 데이터 응답에 대한 출력하는 코드 실행) 해결

콜백 지옥 (Callback hell)

  • 콜백 지옥은 비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제
  • 콜백 지옥을 해결하는 방법에는 Promise나 Async를 사용하는 방법이 있음

3. ES6의 Promise

A promise is an object that may produce a single value some time in the future

  • '프로미스(promise)'란 자바스크립트의 비동기 처리에 사용되는 객체
  • 프로미스는 비동기 작업을 조금 더 편하게 처리 할 수 있도록 ES6 에 도입된 기능
  • ES6 이전에는 비동기 작업을 처리 할 때에는 콜백 함수로 처리를 해야 했었는데, 콜백 함수로 처리를 하게 된다면 비동기 작업이 많아질 경우 코드가 쉽게 난잡해지게 되었음
  • 프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용
// resolve
function fetch() {
	return new Promise(function(resolve, reject) {
      	// 서버에 데이터를 요청하고 데이터를 받으면 resolve 호출
    	resolve('fetch result');
    };
}

fetch().then(function(data){
	console.log(data); // fetch result
});

// reject
function fetchError() {
	return new Promise(function(resolve, reject) {
    	reject('ERROR');
    };
}

fetchError().then().catch(function(error){
	console.log(error); // ERROR
});

프로미스의 3가지 상태

  1. Pending
    비동기 처리 로직이 아직 안료되지 않은 상태
  2. Fulfilled
    비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  3. Rejected
    비동기 처리가 실패하거나 오류가 발생한 상태

프로미스 코드 예제

function getData() {
  return new Promise(function(resolve, reject) {
    $.get('url 주소/products/1', function(response) {
      if (response) {
        resolve(response);
      }
      reject(new Error("Request is failed"));
    });
  });
}

// 위 $.get() 호출 결과에 따라 'response' 또는 'Error' 출력
getData().then(function(data) {
  console.log(data); // response 값 출력
}).catch(function(err) {
  console.error(err); // Error 출력
});

Promise Chaining

  • 프로미스는 여러 개의 프로미스를 연결하여 사용할 수 있음
  • then() 메서드를 호출하고 나면 새로운 프로미스 객체가 반환되기에 아래와 같이 체이닝이 가능함
new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})
.then(function(result) {
  console.log(result); // 1
  return result + 10;
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

4. ES6의 async & await

  • 기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완해줌!
  • 함수 앞에 async라는 예약어를 붙이고, 함수 내부 로직 중에 HTTP 통신을 하는 등의 비동기 처리 코드 앞에 await를 붙임 (비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한대로 동작함에 유의!)
function fetchItems() {
  return new Promise(function(resolve, reject) {
    var items = [1,2,3];
    resolve(items)
  });
}

async function logItems() {
  var resultItems = await fetchItems();
  console.log(resultItems); // [1,2,3]
}



@KneeHunter
안녕하세요, 많이 답답하셨을텐데, 우선 늦은 답변에 대하여 사과드립니다.
이제 보니 Promise 에 대한 설명이 많이 부실했었네요. 초심자의 시각으로 생각하지 않고 제가 너무 대충 설명했었던것 같네요

말씀 주신 문제점들 반영하여 해당 강의는 업데이트하도록 하겠습니다.
질문들에 대한 답변입니다..

increaseAndPrint 콜백 지옥의 파라미터에 대한 설명 작동 흐름 설명
초반의 increaseAndPrint 함수에선 첫번째 파라미터 n에 숫자를 받아오고 두번째 파라미터에선 n에 1을 더한 결과값을 인자로 넣어서 호출 할 함수를 받아옵니다. increaseAndPrint가 호출되고나서 또 다시 자기 자신을 호출하기 위해 연달아서 increaseAndPrint가 작성됐습니다.

해당 코드에서는 <increaseAndPrint 호출 -> n 에 1 더함 -> 더한 값을 가지고 다시 increaseAndPrint 호출>이 값이 5가 될때까지 반복됩니다.

resolve 와 reject가 왜 나왔는가
Promise를 만들때에는 resolve와 reject를 파라미터로 받아오는 함수를 인자로 넣어주어야 합니다. async/await 부분에서 질문주셨던건데, reject는 사용하지 않는다면 생략 될 수 있습니다. resolve 는 처리가 성공됐을때 결과 값을 설정해주고 reject는 실패됐을 때 오류를 설정해줍니다.

catch, console.error
Promise 의 catch 함수는 앞서 만든 Promise 에서 reject 한 값을 받아올 때 사용합니다. 여기서 e 는 reject 에 인자로 넣어준 값입니다.

console.error는 console.log 와 달리 빨간색으로 나오게 됩니다. 콘솔에서 에러가 난 것을 더욱 쉽게 알 수 있게 해주죠.

increaseAndPrint 의 파라미터에 콜백이 사라진 이유
콜백을 없앤 이유는 우리가 콜백을 통해서 그 다음 처리 할 작업을 넣어주지 않고 Promise 의 then 을 통해서 하기 위함이었습니다.

error 를 따로 만든 이유
error 에 ValueIsFiveError 라는 이름을 설정해주기 위함이었습니다.
제가 n+1 값이 "반환" 된다고 표현을 했었는데 이 의미가 정확히 'return' 한다는 의미는 아니었고 정확히는 Promise의 결과값으로 n+1을 설정한다는게 더 옳은 표현일 것 같네요. Mozilla 문서에서는 이를 "이행했다" 라는 표현을 사용하기도 합니다.

then
increaseAndPrint(0)의 결과 1을 .then 의 파라미터 n으로 받아와서 다시 increaseAndPrint를 호출합니다.

.then 에서 Promise 를 반환하면 .then 을 다시 연달아서 사용 할 수 있습니다.

Codesandbox 이상한 현상
저장할때마다 코드가 자동으로 다시 실행됩니다. 그런데 그 과정에서 이미 실행된 Promise들이 사라지지 않고 잔존해있어서 저런 현상이 발생했었고 중요한건 아니여서 그냥 넘겼었습니다. 다만, 설명을 하지 않았을때 혼란을 줄 거라 예상을 못했었네요.

node 에서 왜 오류가 나는지
Node 에서 프로미스에서 발생한 에러를 .catch 하지 않으면 "UnhandledPromiseRejectionWarning" 라는 경고 메시지가 뜹니다. 브라우저에서는 "Uncaught (in promise) ValueIsFiveError" 라는 오류가 뜹니다.

catch 왜 했는지
catch를 한 이유는, 에러가 발생했을때 특정 작업을 하기 위함입니다.

catch를 안했을땐 "Uncaught (in promise) ValueIsFiveError" - ValueIsFiveError 가 잡히지 않았다.
했을땐

했을땐 "ValueIsFiveError" 라고 나타납니다.

즉 콘솔에 출려되는 결과가 다릅니다.

함수를 넣어주기
이 부분 설명이 모호하긴 했네요. 제가 그냥이란 말을 저렇게 많이 하는지도 몰랐습니다..ㅎㅎ;
.then 에 인자로 넣어주어야 하는 것은 함수입니다.

.then 에서 새로 함수를 선언하지 않고 increaseAndPrint 를 넣어주면 앞서 호출한 increaseAndPrint 의 결과값을 다시 increaseAndPrint의 인자로 넣어서 호출하게 됩니다.

파라미터를 여러개 받아와야 하는 경우에는 함수를 새로 선언해야 합니다.

sync와 await는 셋뚜 🎀

  • sync한 함수 내에서 여러개의 await를 쓰는 경우
  • await와 thanable객체
  • 여러 개의 promise를 기다려야 하는 경우

프로미스 객체의 타입스크립트 타입

  • Visual Studio Code.app/Contents/Resources/app/extensions/node_modules/typescript/lib/lib.es5.d.ts
/**
 * Represents the completion of an asynchronous operation
 */
interface Promise<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    /**
     * Attaches a callback for only the rejection of the Promise.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of the callback.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

Reference

profile
#의식의흐름 #순간순간 #생각의스냅샷

0개의 댓글