-
[포스코x코딩온] 비동기 처리, 콜백, Promise, await[포스코x코딩온] 웹개발자 풀스택 부트캠프8기 2023. 7. 29. 14:59728x90
비동기 처리
- setTimeout(code, delay) 함수 이용 : delay 동안 기다리다가 code 함수 실행
console.log(1); setTimeout(function () { console.log(2); }, 5000); console.log(3); setTimeout(function () { console.log(4); }, 2000); // 실행해보면 1-> 3 -> 4 -> 2로 나온다. // 실행과정은 1이 출력되고 2가 출력되어하는데 5초 뒤에 실행되야 한다. // 그 사이에 3 이랑 4가 출력되는 것이다.
- 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성
- 비동기 처리를 하는 이유
- 서버로 데이터를 요청 시, 서버가 언제 그 요청에 대한 응답을 줄 지 모르는데 마냥 다른 코드를 실행 안하고 기다릴 수 없기 때문이다.
- 만약 에러가 나면 계속 응답을 기다려야 함으로 다른 코드를 먼저 실행시키기 위해서
let product; let price; goMart(); pickDrink(); pay(product, price); function goMart() { console.log("마트 도착 음료를 골라보자"); } function pickDrink() { setTimeout(function () { console.log("선택 완료"); product = "콜라"; price = "1000"; }, 3000); } function pay(product, price) { console.log(`상품명: ${product}, 가격: ${price}`); }
- 실행시키면 아래처럼 상품명과 가격에 undefined가 뜬다.
- goMart()가 실행되고 pickDrink()가 실행되어야 하나 setTimeout() 함수로 인해 pay()가 먼저 실행된다. 그 상태에서 product와 price의 값이 할당되지 않았기에 undefined로 뜨게 된다. 이를 해결하기 위해 callback함수를 사용한다.
콜백(callback) 함수
- Javascript는 함수를 인자로 받고 다른 함수를 통해 반환할 수 있는데, 인자(매개변수)로 대입되는 함수를 콜백함수라 한다.
- 다른 함수의 실행을 끝낸 뒤 실행이 되는 함수
- 함수를 선언할 때 parameter(인자, 매개변수)로 함수를 받아서 쓸 수 있다.
- 사용 이유
- 비동기 방식으로 작성된 함수를 동기 처리하기 위하여
- 독립적으로 수행되는 작업도 있는 반면 응답을 받은 이후 처리되어야 하는 종속적인 작업도 있을 수 있으므로 그에 대한 대응 방법이 필요하다.
- 보통 함수를 선언한 뒤에 함수 파라미터를 맨 마지막에 하나 더 선언해주는 방식으로 정의
//콜백함수 사용 예시 //메인 함수, 함수 타입 파라미터 맨 마지막에 하나 더 선언 function mainFunc(param1, param2, callback) { //콜백 함수에 결과 전달 const result = param1 + param2; console.log("mainFunc"); callback(result); } function callbackFunc(param) { console.log(param); } mainFunc(1, 2, callbackFunc); // mainFunc에서 param1에 1, param2에 2, callback에 callbackFunc가 할당되었다. // 그렇게 되면 result는 3이 나오고 callbackFunc의 param에 3이 전달된다.
- 이를 사용하여 상품명과 가격에 할당하는 값을 띄울 수 있다.
let product; let price; goMart(); pickDrink(pay); function goMart() { console.log("마트 도착 음료를 골라보자"); } function pickDrink(callback) { setTimeout(function () { console.log("선택 완료"); product = "콜라"; price = 1000; callback(product, price); }, 3000); } function pay(product, price) { console.log(`상품명: ${product}, 가격: ${price}`); }
- 단점(콜백 지옥(Callback Hell))
- 비동기 프로그래밍 시 발생하는 문제
- 함수의 매개변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기가 너무 깊어지는 현상
- 가독성이 떨어지고 코드 수정 난이도가 상승한다.
- 아래 코드처럼 계속 속성 변경을 계속하기 위해서 계속 들여써야 하는 문제점이 발생한다.
//callback hell //1초마다 배경색 변경 //빨 -> 주 -> 노 -> 초 -> 파 setTimeout(function () { document.querySelector("body").style.backgroundColor = "red"; setTimeout(function () { document.querySelector("body").style.backgroundColor = "orange"; setTimeout(function () { document.querySelector("body").style.backgroundColor = "yellow"; setTimeout(function () { document.querySelector("body").style.backgroundColor = "green"; setTimeout(function () { document.querySelector("body").style.backgroundColor = "blue"; }, 1000); }, 1000); }, 1000); }, 1000); }, 1000);
- 이러한 단점을 해결하기 위해 Promise 사용
Promise
- 비동기 함수를 동기 처리하기 위해 만들어진 객체
- 성공과 실패를 분리하여 반환
- 비동기 작업이 완료된 이후에 다름 작업을 연결시켜 진행할 수 있는 기능을 가진다.
- Promise의 상태
- Pending(대기) : Promise를 수행 중인 상태
- Fulfilled(이행) : Promise가 Resolve 된 상태(성공)
- Rejected(거부) : Promise가 지켜지지 못한 상태. Reject된 상태(실패)
- Settled : fulfilled 혹은 rejected로 결론이 난 상태
- Promise는 두 가지 콜백 함수를 가짐
- resolve(value) : 작업이 성공(fulfilled)한 경우, 결과로 value 호출 -> .then() 메서드 실행
- reject(error) : 에러(rejected)발생 시 에러 객체를 나타내는 error와 함께 호출 -> .catch() 메서드 실행
//promise 객체를 반환하는 promise1 함수 정의 function promise1(flag) { return new Promise(function (resolve, reject) { if (flag) { resolve("promise 상태는 fulfilled, then으로 연결, 이때 flag값은 true"); } else { reject("promise 상태는 rejected, catch로 연결, 이때 flag값은 false"); } }); } console.log(promise1(true)); // 실행 결과 : Promise { 'promise 상태는 fulfilled, 이때 flag값은 true' } promise1(true) .then(function (result) { console.log(result); }) .catch(function (err) { //에러 처리 console.log(err); }); //실행 결과 : promise 상태는 fulfilled, then으로 연결, 이때 flag값은 true promise1(false) .then(function (result) { console.log(result); }) .catch(function (err) { //에러 처리 console.log(err); }); //실행 결과: promise 상태는 rejected, catch로 연결, 이때 flag값은 false
- console.log(promise1(true))로 실헹하면 Promise{ 'promise 상태는 fulfilled, 이때 flag값은 true'}로 객체 형식으로 나옴
- 아래 코드를 실행시켜 보면 if ~ else 문에서 resolve가 실행되면 then으로 연결되고, reject가 실행되면 catch로 연결되는 것을 확인할 수 있다.
let product; let price; goMart(); pickDrink() .then(pay) .catch((err) => { console.log(err); }); function goMart() { console.log("마트 도착 음료를 골라보자"); } function pickDrink() { return new Promise(function (resolve, reject) { setTimeout(function () { console.log("선택 완료"); product = "콜라"; price = 3000; if (price <= 2000) { resolve(); } else { reject("돈이 부족해요."); } //작업 성공(resolve) 시 then으로 연결, 실패(reject) 시 catch로 연결 }, 3000); }); } function pay() { console.log(`상품명: ${product}, 가격: ${price}`); }
- Promise 체이닝
- then 메서드를 연속해서 사용이 가능하다.
- 순차적으로 작업이 가능하다.
- 마지막에 catch 구문으로 예외처리를 한번에 할 수 있다.
//Promise 체이닝 //체이닝 사용 안 한 경우 //(4+3)*2-1 연산 function add(n1, n2, callback) { setTimeout(function () { let result = n1 + n2; callback(result); }, 1000); } function mul(n, callback) { setTimeout(function () { let result = n * 2; callback(result); }, 700); } function sub(n, callback) { setTimeout(function () { let result = n - 1; callback(result); }, 500); } //add -> mul -> sub add(4, 3, function (x) { console.log("1: ", x); mul(x, function (y) { console.log("2: ", y); sub(y, function (z) { console.log("3: ", z); }); }); }); //######################################### //체이닝 사용 한 경우 //장점 : then 메서드를 연속해서 사용 가능 -> 순차적으로 작업 가능 //예외처리 간편 -> 마지막에 catch 구문으로 한 번에 처리 가능 function add(n1, n2) { return new Promise(function (resolve, reject) { setTimeout(function () { let result = n1 + n2; resolve(result); }, 1000); }); } function mul(n) { return new Promise(function (resolve, reject) { setTimeout(function () { let result = n * 2; resolve(result); }, 700); }); } function sub(n) { return new Promise(function (resolve, reject) { setTimeout(function () { let result = n - 1; //resolve(result); reject(new Error("에러 처리")); }, 500); }); } add(4, 3) .then(function (result) { console.log("1:", result); return mul(result); }) .then(function (result) { console.log("2:", result); return sub(result); }) .then(function (result) { console.log("3:", result); }) .catch(function (err) { console.log(err); });
async/await
프로미스 기반 코드를 좀 더 쓰기 쉽고 읽기 쉽게 하기 위해 등장하였으며 비동기 처리 패턴 중 가장 최근에 나온 문법
- async
- 함수 앞에 붙여 Promise를 반환
- 프로미스가 아닌 값을 반환해도 프로미스로 감싸서 반환
- await
- 프로미스 앞에 붙여 프로미스가 다 처리될 때까지 기다리는 역할을 하며 결과는 그 후에 반환
// async / await //구조 //함수 앞에 async 키워드 붙이기 //비동기 처리 메서드 앞에 await 붙이기 (기다려줄 것) //async, await 항상 같이 쓰기 let product; let price; exec(); function goMart() { console.log("마트 도착 음료를 골라보자"); } function pickDrink() { return new Promise(function (resolve, reject) { setTimeout(function () { console.log("선택 완료"); product = "콜라"; price = 1000; if (price <= 2000) { resolve(); } else { reject("돈이 부족해요."); } }, 3000); }); } function pay() { console.log(`상품명: ${product}, 가격: ${price}`); } async function exec() { goMart(); //1번으로 실행 await pickDrink(); //2번으로 실행 pay(); //3번으로 실행 }
728x90'[포스코x코딩온] 웹개발자 풀스택 부트캠프8기' 카테고리의 다른 글
[포스코x코딩온] Form 전송(body-parser, get, post, form validation, nodemon) (0) 2023.08.01 [포스코x코딩온] Javascript 고급(구조분해할당, ...연산자, 클래스) (0) 2023.07.31 [포스코x코딩온] 서버 만들기(http모듈, express, ejs, 템플릿 엔진) (0) 2023.07.28 [포스코x코딩온] 모듈(Module) (0) 2023.07.27 [포스코x코딩온] Node.js의 기본 개념 및 시작 (1) 2023.07.27