일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- firebaseui
- 프로토타입
- React
- css
- 리액트
- iP
- Rest
- mac
- Spread
- 기초
- 커스텀알락
- 구조분해할당
- youtube iframe
- 파이어베이스로그인
- react-firebaseui
- nvm
- JS
- TCPvsUDP
- 자바스크립트
- yarn-berry
- Next.js
- 백준
- nvmrc
- 다리놓기
- leetcode977
- leetcode189
- react-native
- 파이썬
- Python
- 커스텀알림
- Today
- Total
JadeCode
[리뷰] 비동기 본문
비동기 쉽게 이해하기
커피숍 가면 주문 후 진동벨로 음료를 받아본 적이 있을 것이다. 진동벨도 없고 주문번호도 받지 않았을 때는 어땠을까?
한 사람의 주문이 들어가고 음료가 나온 후 새로운 사람이 주문을 했을 것이다. 이를 blocking이라 하는데, 하나의 작업이 끝날 때 까지 이어지는 작업을 막는 것 이다. 이러한 상황을 동기적이라고 한다.
다시 현재로 돌아와서 진동벨이 있을 때를 비동기적이라고 한다.
일단 동기, 비동기 라는 말이 어렵게 느껴지지만 한 마디로 정의하자면 작업이 끝날 때 까지 기다리지 않고 계속 작업을 이어나가는 것이 비동기이다.
전화 | 문자 |
하던 일을 멈추고 받아야 한다(blocking) | 확인 후, 나중에 답장할 수 있다(non-blocking) |
요청에 대한 결과가 동시에 일어난다(synchronous) | 요청에 대한 결과가 동시에 일어나지 않는다(asynchronous) |
비동기의 주요 사례
- DOM Element의 이벤트 핸들러
- 마우스, 키보드 입력(click, keydown 등)
- 페이지 로딩 (DOMContentLoaded 등)
- 타이머
- 타이머 API(setTimeout 등)
- 애니메이션 API (requestAnimationFrame)
- 서버에 자원 요청 및 응답
- fetch API
- AJAX(XHR)
TIMER API
// 타이머 관련 API
// setTimeout(callback,millisecond)
// 일정 시간 후에 함수를 실행
// - 매개변수: 실행할 콜백 함수, 콜백 함수 실행 전 기다려야 할 시간 (밀리초)
// - return 값: 임이의 타이머 ID
setTimeout(function(){
console.log("5초 후 실행");
}, 5000);
// clearTimeout(timerId)
// setTimeout 타이머를 종료
// - 매개변수 : 타이머 ID
// return 값 : 없음
const timer = setTimeout(function(){
console.log("10초 후 실행");
}, 10000);
clearTimeout(timer);
// setTimeout이 종료됨
// setInterval(callback, millisecond)
// 일정 시간의 간격을 가지고 함수를 반복적으로 실행
// - 매개변수: 실행할 콜백 함수, 반복적으로 함수를 실행시키기 위한 시간 간격 (밀리초)
// - return 값 : 임의의 타이머 ID
setInterval(function(){
console.log("1초마다 실행");
}, 1000);
// clearInterval(timerId)
// setInterval 타이머를 종료
// - 매개변수 : 타이머 ID
// - return 값 : 없음
const timer = setInterval(function(){
console.log("5초마다 실행");
}, 5000);
clearInterval(timer);
// setInterval이 종료됨.
비동기로 작동하는 코드 제어 방법 1 : callback 함수
// 비동기함수
const printString = (string, callback) => {
setTimeout(function () {
console.log(string);
callback();
}, Math.floor(Math.random() * 100) + 1);
};
//콜백함수를 통해 동기적으로 작동
const printAll = () => {
printString('A', () => {
printString('B', () => {
printString('C', () => {});
});
});
};
장점 : 비동기 코드의 순서를 제어할 수 있음
단점 : 코드가 길어질 수록 복잡해지고 가독성이 낮아지는 callback hell이 발생할 수 있음
비동기로 작동하는 코드 제어 방법 2 : Promise
Promise는 클래스이기 때문에 new 키워드를 통해 Promise객체를 생성한다. resolve함수와 reject함수를 인수로 전달받음.
Promise 객체가 생성되면 executor는 자동으로 실행되고 작성했던 코드들이 작동된다. 코드가 정상적으로 처리가 되었다면 resolve 함수를 호출하고 에러가 발생했을 경우에는 reject 함수를 호출하면 됨.
Promise 객체의 내부 프로퍼티
new Promise가 반환하는 Promise 객체는 state, result 내부 프로퍼티를 갖는다. 하지만 직접 접근할 수 없고 .then, .catch, .finally 의 메서드를 사용해야 접근이 가능하다.
State
기본 상태는 pending(대기) . 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동했다면 fulfilled (이행)로 변경이 되고, 에러가 발생했다면 rejected (거부)가 된다.
Result
기본 상태는 undefined. 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동하여resolve(value)가 호출되면 value로, 에러가 발생하여 reject(error)가 호출되면 error로 변한다.
let promise = new Promise((resolve, reject) => {
// resolve의 인자에 값을 전달할 수도 있음.
resolve(value);
// reject의 인자에 에러메세지를 전달할 수도 있음.
reject(error);
});
Then
executor에 작성했던 코드들이 정상적으로 처리가 되었다면 resolve함수를 호출하고 .then 메서드로 접근할 수 있음. 또한 .then 안에서 리턴한 값이 Promise면 Promise의 내부 프로퍼티 result를 다음 .then의 콜백 함수의 인자로 받아오고, Promise가 아니라면 리턴한 값을 .then의 콜백 함수의 인자로 받아올 수 있음.
Catch
executor에 작성했던 코드들이 에러가 발생했을 경우에는 reject함수를 호출하고 .catch메서드로 접근할 수 있음.
Finally
executor에 작성했던 코드들이 정상 처리 여부와 상관 없이 .finally 메서드로 접근할 수 있음.
let promise = new Promise((resolve, reject) => {
resolve("성공했다");
// reject(new Error("에러")); 둘 중 하나
});
promise.then(value => {
console.log(value);
// "성공했다"
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log("성공이든 실패든 작동!");
})
promise chaining
promise chaining이 팔요한 경우는 비동기 작업을 순차적으로 진행해야 하는 경우. Promise chaining이 가능한 이유는 .then, .catch, .finally 의 메서드들은 Promise를 반환하기 때문이다. 따라서 .then을 통해 연결할 수 있고, 에러가 발생할 경우 .catch 로 처리하면 된다.
let promise = new Promise(function (resolve, reject) {
resolve('성공');
// reject('실패');
});
promise
.then((value) => {
console.log(value);
return '성공';
})
.then((value) => {
console.log(value);
return '성공';
})
.then((value) => {
console.log(value);
return '성공';
})
.catch((error) => {
console.log(error);
return '실패';
})
.finally(() => {
console.log('성공이든 실패든 작동!');
});
Promise.all()
여러개의 비동기 작업을 동시에 처리하고 싶을 때 사용.
const promiseOne = () => new Promise((resolve, reject) => setTimeout(() => resolve('1초'), 1000));
const promiseTwo = () => new Promise((resolve, reject) => setTimeout(() => resolve('2초'), 2000));
const promiseThree = () => new Promise((resolve, reject) => setTimeout(() => resolve('3초'), 3000));
// 기존
const result = [];
promiseOne()
.then(value => {
result.push(value);
return promiseTwo();
})
.then(value => {
result.push(value);
return promiseThree();
})
.then(value => {
result.push(value);
console.log(result);
// ['1초', '2초', '3초']
})
// promise.all
Promise.all([promiseOne(), promiseTwo(), promiseThree()])
.then((value) => console.log(value))
// ['1초', '2초', '3초']
.catch((err) => console.log(err));
// 하나라도 에러가 발생하게 되면 나머지 Promise의 state와 상관없이 즉시 종료됨.
Promise.all([
new Promise((resolve, reject) => setTimeout(() => reject(new Error('에러1'))), 1000),
new Promise((resolve, reject) => setTimeout(() => reject(new Error('에러2'))), 2000),
new Promise((resolve, reject) => setTimeout(() => reject(new Error('에러3'))), 3000),
])
.then((value) => console.log(value))
.catch((err) => console.log(err));
// Error: 에러1
인자로는 배열을 받음. 해당 배열에 있는 모든 Promise에서 executor 내 작성했던 코드들이 정상적으로 처리가 되었다면 결과를 배열에 저장해 새로운 Promise를 반환.
Promise chaining을 사용했을 경우 코드들이 순차적으로 동작되기 때문에 총 6초의 시간이 걸림. 하지만 Promise.all()을 사용하면 3초안에 모든 작업이 종료됨.
Promise Hell
Promise역시 콜백 함수와 마찬가지로 코드가 길어질수록 복잡해지고 가독성이 낮아지는 Promise Hell이 발생할 수 있음
Async/Await
JavaScript는 ES8에서 async/await 키워드를 제공. 이를 통해 복잡한 Promise코드를 간결하게 작성할 수 있게 됨.
함수 앞에 async키워드를 사용하고 async함수 내에서만 await키워드를 사용하면 됨. 이렇게 작성된 코드는 await키워드가 작성된 코드가 동작하고 나서야 다음 순서의 코드가 동작하게 됨.
// 함수 선언식
async function funcDeclarations() {
await 작성하고자 하는 코드
...
}
// 함수 표현식
const funcExpression = async function () {
await 작성하고자 하는 코드
...
}
// 화살표 함수
const ArrowFunc = async () => {
await 작성하고자 하는 코드
...
}
const printString = (string) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
console.log(string);
}, Math.floor(Math.random() * 100) + 1);
});
};
const printAll = async () => {
await printString('A');
await printString('B');
await printString('C');
};
printAll();
'개발 > 웹' 카테고리의 다른 글
리액트 (0) | 2022.06.02 |
---|---|
[리뷰] Fetch, Axios (0) | 2022.05.31 |
[리뷰] 프로토타입 (0) | 2022.05.27 |
[리뷰] 객체 지향 프로그래밍 (0) | 2022.05.26 |
[리뷰] 고차함수 (0) | 2022.05.24 |