게시글 하나에 너무 많은 주제를 담는 것 같기도 합니다.
하지만 제목의 키워드들은 각각에 대해 자세히 이야기 하는 것 보다,
서로의 연관성에 대해 이해 하는 것이 먼저 필요할 것 같습니다.
이에 본 게시글은,
각각이 어떻게 연관되어있는지 왜 함께 언급되는지에 대해,
제 나름의 이해를 설명하려 합니다.
내용 중 잘못 된 것이 있다면,
댓글로 알려주시면 도움이 되겠습니다.
- 동기와 비동기 -
동기적인 실행은 요청한대로 응답하는 방식을 말합니다.
해당 요청에 대한 응답이 마무리되지 않는다면 다른 작업을 진행하지 않습니다.
비동기적 실행도 요청한대로 응답합니다.
하지만 해당 요청에 대한 응답이 마무리 되지 않더라도 다른 작업을 진행합니다.
MDN의 정의에도 나와있듯이 자바스크립트는 single-threaded language입니다.
때문에 기본적으로 동기적인 방식으로 script가 실행합니다.
동기적인 script 실행에는 중요한 특징이 있습니다.
무거운 연산을 실행 할 경우,
해당 연산이 끝나기 전까지 다른 작업을 진행 할 수 없다는 것입니다.
이때문에 사용자의 필요에 따라,
비동기적으로 script를 실행 하는 방식도 필요합니다.
Web API을 통한 데이터 fetching, setTimeout 등이 대표적인 예 입니다.
문제는 동기적 실행과 비동기적 실행이 섞여있는 경우,
그 실행순서를 예측하기 어렵고 예상치 못한 버그가 발생하는 경우가 있다는 것입니다.
예를들어 하기와 같은 소스코드를 실행한다고 가정합니다.
1
2
3
4
5
6
7
8
9
10
11
|
function test() {
setTimeout(() => {
variable = '3번 줄';
console.log(variable);
}, 500);
let variable = '7번 줄';
console.log(variable);
}
test();
|
cs |
겉으로 보기에는 2~5번 줄의 코드블럭이 먼저 실행되고,
7~8번줄의 코드블럭이 나중에 실행되어야 할 것 같습니다만,
실행결과는 하기와 같이 정 반대입니다.
이러한 점때문에,
코드의 실행순서를 명확히 제어 할 필요가 있습니다.
동기적 실행,
비동기적 실행 각각의 특징이 있고 쓰임새가 다릅니다.
다만 중요한 것은,
코드의 실행 순서는 개발자의 의도대로 이루어져야 한다는 점이겠지요.
하기에서 다룰,
콜백함수, Promise, Async와 Await등은 모두,
코드 실행순서 및 결과 제어가 중요 포인트 인 것 같습니다.
가장 간단히 코드실행 순서를 제어하는 방법이 바로 콜백함수입니다.
- 콜백함수 -
콜백함수는 함수의 결과값을 인자로 받는 함수를 의미합니다.
콜백함수의 정의자체는 비동기적 의미를 가지지 않습니다.
MDN에 기재되어있는 콜백함수의 정의는 하기와 같습니다.
콜백함수는 다른 함수의 실행결과값을 함수의 인자로 받기 때문에,
그 자체가 코드의 실행순서를 제어한다고 말 할 수 있습니다.
다만 가독성이 떨어지고,
디버깅이 어렵고,
모듈화가 어렵고,
모든 실행결과를 if문으로 작성해야하는 단점이 있습니다.
- Promise -
MDN에 기재되어있는 Promise의 정의는 하기와 같습니다.
위 그림에 나와있는대로,
Promise는 비동기적 작업과 그 결과값을 표현하는 객체입니다.
Promise의 사용 흐름을 그림으로 표현하면 하기와 같습니다.
Promise의 상태는 pending, fulfilled, reject로 나타냅니다.
상태에 따라 then, catch, finally method를 통해 결과를 제어합니다.
resolve함수를 Promise내에 지정하여,
Promise가 fulfill될 경우 then method를 통해 해당 함수를 호출합니다.
reject함수를 Promise내에 지정하여,
Promise가 reject될 경우 catch method를 통해 에러를 제어합니다.
finally method는 Promise결과와는 관계없이 가장 마지막에 무조건 실행됩니다.
하기와 같은 간단한 소스코드를 작성하고 실행해보았습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function promiseTest(param) {
return new Promise((resolve, reject) => {
if (!param) {
reject(new Error('parameter needed'));
}
resolve();
});
}
promiseTest()
.then(() => console.log('promise fulfilled')) // promiseTest가 정상적으로 호출 될 경우 실행
.catch(console.error) // promiseTest호출 시 에러가 발생 될 경우 실행
.finally(() => console.log('you finally get here')); // 호출 결과에 관계없이 무조건 실행
|
cs |
promiseTest에 인자를 입력 할 경우(promise fulfilled)의 결과는 하기와 같습니다.
promiseTest에 인자를 입력 하지 않을 경우(promise rejected)의 결과는 하기와 같습니다.
- Async와 Await -
MDN에 기재되어있는 Async의 정의는 하기와 같습니다.
Async의 경우,
정의못지않게 하기의 내용도 중요합니다.
Async의 경우,
실행결과로 promise 객체가 반환되기 때문에 promise객체를 대신하여 사용할 수 있는 것입니다.
내용을 확인하기 위해,
위의 promise에서 예시로 들었던 소스코드를 Async를 활용하여 변경해보았습니다.
1
2
3
4
5
6
7
8
9
10
11
12
|
const promiseTest = async (param) => {
try {
if (!param) {
throw new Error('parameter needed');
}
console.log('promise fulfilled'); // promiseTest가 정상적으로 호출 될 경우 실행
} catch (error) {
console.error(error); // promiseTest호출 시 에러가 발생 될 경우 실행
} finally {
console.log('you finally get here'); // 호출 결과에 관계없이 무조건 실행
}
};
|
cs |
에러가 발생하지 않을 시의 console은 하기와 같습니다.
에러가 발생할 시의 console은 하기와 같습니다.
promise를 사용할 때, Async를 사용할 때 동일한 결과를 얻을 수 있었습니다.
위와 같이 흐름 자체를 먼저 이해하니,
개념정리에 많은 도움이 되는 기분입니다.
아직 이해가 불완전한 부분도 있지만,
차차 수정 및 보완해가며 완전히 이해하도록 해야겠습니다.
'웹개발자공부 > Javascript' 카테고리의 다른 글
Javascript - Logical AND (&&), Logical OR (||)의 정의 및 사용 예시 (0) | 2022.12.29 |
---|---|
Javascript - this 사용 유무에 따른 변수 호출 결과 차이 확인 (0) | 2022.12.28 |
Javascript - Nullish coalescing operator(??)의 정의 및 사용 예시 (0) | 2022.12.16 |
Javascript - Optional chaining (?.)의 정의 및 사용 예시 (0) | 2022.12.16 |
Javascript - forEach, map, filter의 정의, 차이 및 사용 예시 (0) | 2022.12.15 |