[JavaScript 응용] 동기 & 비동기 / Promise & 콜백 지옥 탈출 / async & await
🔮 동기 & 비동기
- 용어 정리
- 스레드 : 연산을 수행하는 일꾼 같은 역할.
- 블로킹 작업 : 스레드가 한가지 일만 하고 다른 일을 할 수 없는 방식
- 논블로킹 작업 : 하나의 작업을 하면서 다른 작업도 할 수 있는 방식 = 비동기 2번째 방식( 그냥 동시에 실행시키는 방식 )
- 동기적 방식 : 지시 순서대로 작업을 수행하는 방식
- 비동기 방식 : 멀티 쓰레드를 사용하거나 그냥 동시에 실행시키는 방식. 자바 스크립트의 경우 멀티 쓰레드 사용이 불가하므로 동시에 실행시키는 방식을 택한다.
📍 setTimeout() : 비동기 방식 사용하기
function taskA(a, b, cb) {
setTimeout(() => {
const res = a + b;
cb(res);
}, 3000);
}
taskA(3, 4, (res) => {
console.log("A Task Result : " + res);
});
- taskA : taskA라는 함수
- a, b : 매개 변수
- cb : 콜백 함수
-
setTimeout(() => {}, ms) : 동시 작업을 할 수 있도록 해줌. ms부분에는 몇 ms 뒤 계산된 값을 출력할 것인지 선택.
- 실행 순서
- taskA(3, 4, f) 를 통해서 taskA 메소드를 호출
a = 3
,b = 4
,cb = (res) => {console.log("A Task Result : " + res);}
매개 변수 연결. cb라는 함수( js는 딱히 데이터형을 정해주는 게 없기 때문에 이 부분이 헷갈림,,ㅜ 함순지, 변순지, 리턴이 있는지, 없는지,,등 여튼) 는 매개변수가 res이고 res를 출력해주는 함수이다.- res는 뭐야? 현재 메인에서 taskA로 매개변수들이 넘어갔는데 거기서 나온 결과값이 cb함수의 인자가 된 것!
- 🌗 taskA -> 인자 전달 -> res = a + b -> cb(res) -> 메인에 매개변수 res로 인자가 넘어감 -> 출력
📍비동기 방식 실행 방식
js 엔진에는 동기 방식인 경우 heap, CallStack 이 있다. 비동기 방식의 경우 Heap, CallStack, Callback Queue, Web API 로 나뉜다.
function taskA(a, b, cb) {
setTimeout(() => {
const res = a + b;
cb(res);
}, 3000);
}
taskA(3, 4, (res) => {
console.log("A Task Result : " + res);
});
console.log("A code end");
>>>
A code end
A Task Result : 7
- 코드 실행 = CallStack에 MainContext가 들어감
- taskA() 메소드를 CallStack에 올림
- taskA() 메소드 안에 setTimeout() 메소드가 있음(cb()랑 같이) => 둘 다 CallStack에 올림
- setTimeOut, cb를 Web API로 넘겨 => 여기서 3초 기다려(setTimeout은 비동시 함수니까)
- 3초 기다리는 동안 taskA함수는 끝나서 CallStack에서 지워짐
- 3초가 지나면 setTimeout 메소드는 web API에서 지워지고 cb는 Callback Queue로 넘어옴
- Event Loop를 통해서 CallStack으로 cb메소드를 넘겨줌 => 실행
- cb 지워, main Context 지워.
- 끝
📍 문제점
비동기 처리방식을 연결해서 처리해주고 싶다면 다음과 같이 콜백 지옥에 빠질 수 있음
taskA(4, 5, (a_res) => {
console.log("A Result: ", a_res);
taskB(a_res, (b_res) => {
console.log("B Result: ", b_res);
taskC(b_res, (c_res) => {
console.log("C Result: ", c_res);
});
});
});
❗ 해결책 ❗
Promise 객체
🔮 Promise 객체 : 콜백 지옥 해결책
📍 콜백 지옥
function isPositive(number, resolve, reject) {
setTimeout(() => {
if (typeof number === "number") {
//비동기 작업 성공 = resolve 상태
resolve(number >= 0 ? "양수" : "음수");
} else {
//실패 = reject
reject("주어진 값이 숫자형이 아닙니다.");
}
}, 2000);
}
isPositive(
[],
(res) => {
console.log("성공적으로 실행됨 : ", res);
},
(err) => {
console.log("실패 하였음 : ", err);
}
);
- isPositive() : 함수 이름
- number : 매개 변수
- resolve : 비동기 작업이 성공했을 때 실행되는 메소드
-
reject : 비동기 작업이 실패했을 때 실행되는 메소드
- 메인 코드
- [] 👉 number
- (res) => console.log 👉 resolve : 즉 이 말은 resolve라는 메소드 이름이고, 구현부는 출력부분이다. 콘솔로그 앞에 있는 res는 매개변수이고 위에 isPositive 메소드에 있는 값은 인자이다.
- 🌗 isPositive 메소드 호출 -> if 문 성립하면 resolve 메소드를 호출 -> 메인에 있는 resolve 실행. oo요딴식
📍 Promise 객체
-
용어 정리
- pending : 대기 상태
- Fulfiled : 정상 완료 = resolve(해결)
- Rejected : 실패 = reject(거부)
promise 객체는 비동기 작업을 하고, 그 작업의 결과를 promise로 반환 받는다. Promise는 비동기를 처리받는 함수로 만드는 것이다.
- Promise 객체의 메소드
- then() : resolve 수행이 된다면 그 결과를 then으로 전달
- catch() : reject 수행 결과를 전달
-
비동기 방식 활용 1.
function isPositiveP(number) { //비동기 작업 수행 함수 const executor = (resolve, reject) => { setTimeout(() => { if (typeof number === "number") { //비동기 작업 성공 = resolve 상태 console.log(number); resolve(number >= 0 ? "양수" : "음수"); } else { //실패 = reject reject("주어진 값이 숫자형이 아닙니다."); } }, 2000); }; const asyncTask = new Promise(executor); return asyncTask; //promise 반환 = 비동기 작업을 하고, 그 작업의 결과를 promise로 반환받음 } const res = isPositiveP(101); res .then((res) => { console.log("작업 성공"); }) .catch((err) => { console.log("error"); });
- executor : resolve 수행인지, reject 수행인지를 판단하는 함수
- if 문 참 -> resolve 수행, resolve가 수행되므로
asyncTask
는 fulFilled 상태이고 매개변수는 양수인 상태. fulFilled(정상 완료) 상태이므로 .then(res) 로 넘어감. res = 양수. - else 문 참 -> reject 수행, res = 주어진 값이 숫자형이 아닙니다. -> .catch 문 수행
- ❗ 헷길리지만,,,resolve() 수행이 anyncTask에 2가지 값을 전달하는 느낌? main에서 res는 양수 를 리턴받는다. 하지만 isPositiveP의 리턴은 asyncTask 지만, resolve()가 실행이 된다면 정상 수행임을 인지(.then 사용), 리턴은 resolve 값 반환 -> 요런 느낌,,?
- 비동기 방식 활용 2.
function taskA(a, b) { return new Promise((resolve, reject) => { setTimeout(() => { const res = a + b; resolve(res); }, 3000); }); }
이렇게 되면 taskA가 리턴하는 값은 정상 완료가 되었는지 아닌지이다. (예시는 일단 무조건 정상 완료를 가정하여 reject() 사용 X)
📍 async / await
-
async : 얘를 붙히면 Promise 객체 반환
async function helloAsync() { return "hello Async"; } console.log(helloAsync()); //promise 객체임 >>> Promise { 'hello Async' }
-
await : 비동기 함수 호출 앞에 붙히면 동기 함수처럼 사용 가능.
댓글남기기