[JavaScript] async & await

최승원·2022년 2월 11일
0

TIL (Today I Learned)

목록 보기
9/21

비동기 처리란?

특정 코드의 실행이 끝날 때까지 기다려주지 않고 다음 코드를 먼저 실행하는 것.
동기보다 복잡하지만 결과가 주어지는데 시간이 걸리더라도 그 시간동안 다른 작업을 할 수 있으므로 자원을 효율적으로 사용할 수 있는 장점이 있다.

function getData() {
	var tableData;
  	// HTTP GET 요청을 날려 1번 상품(product) 정보를 요청
  	// 받아온 데이터는 response 인자에 담김
	$.get('https://domain.com/products/1', function(response) {
		tableData = response;
	});
	return tableData;
}

console.log(getData()); // undefined

callback 함수

callback 함수에는 두 가지 정의가 있다.

1. 다른 함수의 인자로써 이용되는 함수

//hello!
function printHello(){ print('hello'); }
//bye!
function printBye(){ print('bye'); }

//특정 함수를 매개변수로 받아서 3초 뒤에 실행하는 함수
function sleepAndExecute(sleepTimeSecond, callback){
	//sleepTimeSecond 초 만큼 대기
  	sleep(sleepTimeSecond);
  	//전달된 callback 실행
  	callback(); }

//3초 뒤에 hello 출력하기
sleepAndExecute(3, printHello); 
//5초 뒤에 bye 출력하기
sleepAndExecute(5, printBye);

이 때 printHelloprintBye 함수는 sleepAndExcute 함수의 매개변수로 이용되므로 callback 함수라고 할 수 있다.

2. 어떤 이벤트에 의해 호출되어지는 함수

function onCableConnected(){ print("케이블이 연결되었습니다"); };

//케이블이 연결될 때 마다 전달된 onCableConnected가 호출된다고 가정
setOnCableConnected(onCableConnected);

setOnCableConnected가 케이블을 연결할 때마다 호출되므로, onCableConnected는 callback 함수라고 할 수 있다.

이러한 callback 함수를 사용하면 특정 로직이 끝났을 때 원하는 동작을 실행시킬 수 있다.

function getData(callbackFunc) {
	$.get('https://domain.com/products/1', function(response) {
		callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
	});
}

getData(function(tableData) {
	console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});

callback 지옥

서버에서 데이터를 받아와 화면에 표시하기까지 모든 과정을 비동기로 처리한다면?
콜백 안에 콜백을 무는 형식으로 코딩을 하게 된다.

$.get('url', function(response) {
	parseValue(response, function(id) {
		auth(id, function(result) {
			display(result, function(text) {
				console.log(text);
			});
		});
	});
});

Promise

Promise는 new Promise() 메소드를 호출하여 비동기 작업을 생성/시작하는 객체이다.
동작에 대한 결과를 올바르게 줄 수 있다면 resolve, 동작을 실패했다면 reject 함수를 호출한다.

Promise에는 크게 세 가지 상태(states)가 있다.

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

Pending(대기)

먼저 아래와 같이 new Promise() 메서드를 호출하면 대기(Pending) 상태가 된다.

new Promise();

new Promise() 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject이다.

new Promise(function(resolve, reject) {
  // ...
});

Fulfilled(이행)

여기서 콜백 함수의 인자 resolve를 아래와 같이 실행하면 이행(Fulfilled) 상태가 된다.

new Promise(function(resolve, reject) {
  resolve();
});

그리고 이행 상태가 되면(resolve 함수가 실행되면) 아래와 같이 then()을 이용할 수 있다. then()을 이용하여 처리 결과 값을 받을 수 있다.

function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

Rejected(실패)

콜백 함수의 인자 reject를 아래와 같이 호출하면 실패(Rejected) 상태가 된다.

new Promise(function(resolve, reject) {
  reject();
});

실패 상태가 되면(reject 함수가 실행되면) catch()을 이용할 수 있다. 실패한 이유를 catch()로 받을 수 있다.

function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function(err) {
  console.log(err); // Error: Request is failed
});

위의 내용을 종합하여 다음과 같은 Promise 코드를 만들 수 있다.

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.all()

Promise.all()은 모든 Promise가 이행될 때까지 기다렸다가 그 결과값을 담은 배열을 반환하는 메서드이다.

여러개의 Promise가 모두 리졸브 된 후, 다음 로직을 실행해야 하는 경우에 사용한다. 복수의 URL에 request를 보내고, 모든 요청의 응답이 올때 화면을 렌더 해야하는 상황이 그 예시이다.

let urls = [
  'https://www.example.com/users/1',
  'https://www.example.com/product/2',
  'https://www.example.com/article/3'
];

// fetch를 사용해 url을 프라미스로 매핑
let requests = urls.map(url => fetch(url));

// Promise.all은 모든 작업이 리졸브 될 때까지 대기
Promise.all(requests)
  .then(responses => responses.forEach(
    response => alert(`${response.url}: ${response.status}`)
  ));

이 때, 입력된 Promise 배열이 하나라도 리젝트 되면 Promise.all() 또한 리젝트 된다.

async & await

Promise의 가독성 문제를 개선한 비동기 처리 패턴이다.

함수 앞에 async를 붙이면 해당 함수는 항상 Promise를 반환한다.
async 함수 안에서 await 키워드를 만나면 반환된 Promise가 처리(fulfilled 또는 rejected)될 때까지 기다린다. 결과는 그 이후 반환된다.

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]
}

fetchItems 함수를 실행하면 Promise가 이행(fulfilled)되며 결과 값은 items가 된다.
logItems() 함수를 실행하면 fetchItems()의 결과 값이 resultItems 변수에 담긴다.
만약 이 때 await를 사용하지 않았다면, callback 함수나 .then()을 사용해야 했을 것이다.

참조

자바스크립트 async와 await
[Javascript] 비동기, Promise, async, await 확실하게 이해하기
[메모] Promise.all

profile
문의 사항은 메일로 부탁드립니다🙇‍♀️

0개의 댓글