728x90
반응형

13. 비동기 프로그래밍

비동기 처리 방식

동기 처리 방식과 비동기 처리 방식

ex.  커피 전문점에서 커피 주문하고 마시기

  • 동기 처리 방식
    • A라는 사람이 커피를 주문 -> 그 주문을 받아서 커피를 만들고 A에게 넘겨줌
    • 뒤에 아무리 많은 손님이 있어도 한번에 하나의 손님만 처리
    • 주문을 받고 커피를 만드는 것이 한 과정
      -> 대기 줄이 점점 더 길어지고 주문을 처리하는데도 시간이 걸림
  • 비동기 처리 방식
    • A라는 사람이 커피를 주문 -> 그 주문을 주방으로 넘김
    • A에게는 진동벨을 주면서 커피가 완성되면 알려 주겠다고 함
    • 대기하고 있던 B의 주문을 받고 진동벨을 건네줌
    • 중간에 A의 커피가 완성되면 A에게 알려 준다

싱글 스레드 vs 멀티 스레드

스레드(thred)

  • 프로세스에서 작업을 실행하는 단위
  • 싱글 스레드: 한번에 하나의 스레드만 처리
  • 멀티 스레드: 한번에 여러 스레드를 사용

자바스크립트) 싱글 스레드 사용 = 한번에 하나의 작업만 처리할 수 있음

시간이 오래 걸리는 작업이 앞에서 실행 중 -> 그 작업이 끝날 때까지 무작정 기다려야 함

=> 시간 ↓ 함수 먼저 처리 -> 멀티 스레드처럼 동작하게 함


      
// ex.
function displayA() {
console.log("A");
}
function displayB() {
setTimeout(() => console.log("B"), 2000);
}
function displayC() {
console.log("C:);
}
displayA();
displayB();
displayC();

 

=> A, C, B 순서대로 출력 (A, B, C 순서 X)

-> display()를 2초 후에 실행하도록 했기 때문


함수 이름을 콜백으로 사용하기

ex. 사용자가 커피 주문하면 3초 후에 커피가 완성되는 프로그램


      
// 1) 커피 주문 프로그램
function order(coffee) {
console.log(`${coffee} 주문 접수`);
}
// 2) 완성됐다고 알려주는 프로그램
function display(result) {
console.log(`${result} 준비 완료`);
}


      
function order(coffee, callback) {
console.log(`${coffee} 주문 접수`);
setTimeout(() => {
callback(coffee);
}, 3000);
}
function display(result) {
console.log(`${result} 준비 완료`);
}
order("아메리카노", display);

익명 콜백 사용

ex. 1초마다 A -> B -> C -> D -> STOP! 순으로 표시


      
fuction displayLetter() {
console.log("A");
setTimeout( () => {
console.log("B");
setTimeout( () => {
console.log("C");
setTimeout( () => {
console.log("D");
setTimeout( () => {
console.log("stop!");
}, 1000);
}, 1000);
}, 1000);
}, 1000);
}
displayLetter();

 

=> 콜백 지옥: 콜백이 계속 반복되는 상태

소스의 가독성 ↓, 오류가 발생했을 때 디버깅하기 어려움


프로미스(promise)

  • 콜백 안에 콜백, 그 안에 또 콜백 -> 콜백 지옥
  • 콜백을 사용했을 때 복잡함 피하기 위해, ES6부터 프로미스 등장

처리에 성공했을 때 실행할 함수, 성공하지 못했을 때 실행할 함수 미리 약속

프로미스 사용 -> Promise 객체 먼저 만들어야 함


      
new Promise(resolve, reject)
  • 성공했을 때 실행할 함수: resolve()
  • 실패했을 때 실행할 함수: reject()

프로미스) 객체를 생성(제작)하는 부분, 소비하는 부분

프로미스 제작 코드에서 '성공'과 '실패' 확인 후 소비 코드로 알려줌


ex. 피자 주문 흐름

  • likePizzatrue -> 성공했을 때 실행할 함수에 '피자를 주문합니다' 넘김
  • likePizzafalse -> 실패했을 때 실행할 함수에 '피자를 주문하지 않습니다' 넘김

      
// 프로미스 제작 코드
let likePizza = true;
const pizza = new Promise((resolve, reject) => {
if (likePizza) resolve('피자를 주문합니다');
else reject('피자를 주문하지 않습니다');
});

 

프로미스를 실행할 때 사용하는 함수

  • then(): 프로미스에서 성공했다는 결과를 보냈을 때 실행할 소스
  • catch(): 프로미스에서 실패했다는 결과를 보냈을 때 실행할 소스
  • finally(): 프로미스의 성공과 실패에 상관없이 실행할 소스

      
프로미스객체
.then()
.catch()
.finally();

      
// 프로미스 소비 코드
pizza
.then(
result => console.log(result)
)
.catch(
err = console.log(err)
)
.finally(
() => console.log("완료");
);

프로미스 상태

resolve() 함수나 reject() 함수를 매개변수로 받아서 실행하는 객체 

실행하면서 상태가 바뀜

상태 설명
pending 처음 프로미스를 만들면 대기 상태가 됨
fulfilled 처리에 성공하면 이행 상태가 됨
rejected 처리에 성공하지 못하면 거부 상태가 됨

  • 제작코드: fulfilled 상태인지, reject 상태인지
    -> '피자를 주문합니다' or '피자를 주문하지 않습니다' 라는 결괏값 넘겨줌
  • 소비코드: 결괏값을 result or err 같은 변수 이름으로 받아서 사용

커피 주문하고 완료하는 프로미스

  • 프로미스 제작 코드
    • 프롬프트 창 ~> 사용자에게 원하는 커피 입력하게 함
    • 커피 메뉴 입력 -> 화면에 주문이 접수되었다고 표시, 3초 후에 resolve 함수에 커피 메뉴 건내줌
    • 커피 메뉴 입력 X -> 주문이 없다는 오류 메시지 표시
  • 프로미스 소비 코드
    • 성공했으면 display 함수, 실패하면 showErr 함수 실행
    • 화면에 내용을 표시하는 display 함수, 오류를 표시하는 showErr 함수 만듦

      
const order = new Promise((resolve, reject) => {
lef coffee = prompt("어떤 커피 주문?", "아아");
if (coffee != null & coffee != "") {
document.querySelector(".start").innerText = `${coffee} 주문 접수`;
setTimeout(() => {
resolve(coffee);
}, 3000);
} else {
reject("커피를 주문하지 않았습니다");
}
});
order // 현재 상태는 fulfilled, 결괏값: 프롬프트 창에서 받은 입력 내용
// Promise {<fulfilled>: '아아'}
order.then() // 프로미스 반환
// Promise {<fulfilled>: '아아'}

      
// 프로미스 소비 코드
function display(result) {
document.querySelector(".end").innerText = `${result} 준비 완료`;
document.querySelector(".end").classList.add("active");
document.qeurySelector(".start").classList.add("done");
}
function showErr(err) {
document.qeurySelector(".start").innerText = err;
}
order
.then(display)
.catch(showErr);

여러 단계 연결해서 프로그램 만들기

프로그램은 여러 단계 연결해서 사용하는 경우 ↑

ex. 서버에서 학생 자료 가져옴 -> 성공? 가져온 자료를 객체로 만듦

-> 성공? 객체에서 필요한 정보 꺼냄 -> 성공? 화면에 표시...

 

콜백 함수

ex. 피자 만들기: 피자 도우 준비 -> 토핑 올리기 -> 굽기


      
const step1 = (callback) => {
setTimeout(() => {
console.log("피자 도우 준비");
callback();
}, 2000);
}
const step2 = (callback) => {
setTimeout(() => {
console.log("토핑 완료);
callback();
}, 1000);
}
const step3 = (callback) => {
setTimeout(() => {
console.log("굽기 완료");
callback();
}, 2000);
}

      
console.log("피자를 주문합니다.");
step1(function() {
step2(function() {
step3(function() {
console.log("피자가 준비되었습니다.");
}
}
});

 

프로미스 체이닝

프로미스

  • resolvereject 사용해서 성공과 실패에 대한 동작 명확하게 구별 O
  • 함수에 계속해서 함수 포함 X 콜백 지옥에서 벗어날 수 있는 좋은 방법

ex. A, B, C 각각 실행 시간이 다르지만 A 작업이 끝날 때까지 기다렸다가 B 작업을 하고,

다시 B 작업이 끝날 때까지 기다렸다가 C 작업을 해야 한다면?

 

=> 프로미스 체이닝: then()을 사용해 여러 개의 프로미스를 연결하는 것

ex. A.then(B).then(C) = A 프로미스 -return-> .then(B 프로미스) -return-> .then(C 프로미스)


      
// 프로미스 제작
const pizza = () => {
return new Promise((resolve, reject) => {
resolve("피자를 주문");
});
};
pizza() // 프로미스 반환
// Promise {<fulfilled>: '피자를 주문'}

      
// 프로미스 소비
const step1 = (message) => {
console.log(messaeg);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('피자 도우 준비');
}, 3000);
});
};
const step2 = (message) => {
console.log(messaeg);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('토핑 완료');
}, 1000);
});
};
const step3 = (message) => {
console.log(messaeg);
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('굽기 완료');
}, 2000);
});
};

      
pizza()
.then((result => step1(result)) // pizza() 성공 -> step1() 실행
.then((result => step2(result)) // step1() 성공 -> step2() 실행
.then((result => step3(result)) // step2() 성공 -> step3() 실행
.then((result => console.log(result)) // step3() 성공 -> "굽기 완료" 표시
.then(() => {
console.log('피자가 준비되었습니다.');
});


프로미스 소비 코드 줄여쓰기


      
pizza()
.then(result => step(result)
// 줄여쓰기
pizza()
.then(step1)

      
pizza()
.then(step1)
.then(step2)
.then(step3)
.then(console.log)
.then(() => {
console.log("피자가 준비되었습니다.");
});

fetch API

  • 서버에 자료를 요청하거나 자료를 받아올 때 사용하는 API
  • XMLHttpRequset 대신
  • 프로미스 반환

      
fetch(위치, 옵션)
  • 위치: 자료가 있는 URL이나 파일 이름
  • 옵션: GET/ POST 같은 요청 방식 지정 (기본: GET)

async와 await

프로미스: 콜백 지옥이 생기지 않도록 소스를 읽기 쉽게 바꾼 것

프로미스 체이닝은 프로미스 계속 연결해서 사용 -> 콜백 지옥처럼 소스 복잡해질 수 있음

-> 이런 문제 ↓ => async 함수와 await 예약어 등장

async 함수

함수 안에 있는 명령을 비동기적으로 실행할 수 있음


      
async function() {...}

await

  • 프로미스 체이닝을 좀 더 쉽게 작성
  • async 함수에서만 사용할 수 있음

      
// ex
async function whatsYourFavorite() {
}
async function displaySubject(subject) {
return `Hello, ${subject}`;
}
async function init() {
const response = await displaySubject(response);
console.log(result);
}
init();

 

=> whatsYourFavorite() 함수 처리 시간이 얼마 걸리든 기다렸다가 결괏값 -> response에 저장


출처

 

Do it! 모던 자바스크립트 프로그래밍의 정석

 

www.youtube.com

 

728x90
반응형
김앩옹