[JavaScript] 동기식 & 비동기식(Synchronous and Asynchronous)
태그: javascript · Synchronous · Asynchronous · 동기식 · LIFO · 비동기식 · FIFO · Callback · Promise · async · await · Queue · Stack
1. Synchronous and Asynchronous
(1): 특징
JavaScript는 동기식 언어
자바스크립트는 한 번에 하나의 작업을 수행함
단일 스레드(싱글 스레드) 방식
(2): 비동기식이 필요한 이유
웹 페이지가 로딩되거나, 어떠한 동작(Event) 하나가 30초 이상이 걸림.
그렇게 되면, 웹 페이지는 이 동작이 끝날 때까지 화면에 나타나지 않거나 다음 동작을 수행하는데 지장을 주게 된다.
또, 요즘 사용자들은 느리고 응답이 없는 웹 사이트를 원하지 않는다.
그렇기 때문에 자바스크립트가 웹 사이트에서 동작할 때, 비동기적으로 동작할 수 있어야 한다.
브라우저에서의 자바스크립트 실행 환경(Runtime)에서는 자바스크립트 엔진 자체가 제공하지 않는 일부 기능인 DOM 조작이나 AJAX 같은 비동기 처리를 위한 web API를 제공.
또, 이를 제어하기 위해 이벤트 루프(Event Loop), 이벤트 큐(Callback Queue 혹은 task Queue)가 존재함
1) 동기적 실행 (synchronous)
[동기(Synchronous) - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN](https://developer.mozilla.org/ko/docs/Glossary/Synchronous) |
(1). 요청과 그 결과가 동시에 일어난다는 일종의 약속
(2). 요청을 하면 시간이 얼마가 걸리던, 요청한 자리에서 결과가 주어져야 함
- 요청과 결과가 한 자리에서 동시에 일어난다.
- A 노드와 B 노드 사이의 작업 처리 단위 (transaction) 를 동시에 맞춘다.
설계가 간단하고 직관적이나 결과가 주어질 때까지 아무것도 못하고 대기해야 한다.
2) 비동기적 실행 (Asynchronous)
[비동기(Asynchronous) - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN](https://developer.mozilla.org/ko/docs/Glossary/Asynchronous) |
(1) 요청과 결과가 동시에 일어나지 않는다는 약속
- 요청한 그 자리에서 결과가 주어지지 않음
- 노드 사이의 작업 처리 단위를 동시에 맞추지 않아도 됌.
복잡하지만, 결과가 주어지는데 시간이 걸리더라도 그 시간 동안 다른 작업을 할 수 있으므로, 자원을 효율적으로 사용할 수 있다는 장점이 있다.
3) 동기방식 예제
{
function synchronous(callback, delay){
callback();
}
console.log(0);
synchronous(function() {
console.log('hello~');
}, 0);
console.log(1);
}
=> 결과값
[Running] node "/workspaces/codespaces-blank/index.js"
0
hello~
1
[Done] exited with code=0 in 0.048 seconds
코드의 실행과정
- 스택방식 (후입선출 (last in first out, LIFO) 구조)
(1) 먼저 스택에서 index.js 파일을 실행시킨다.
(2) 가장 먼저 실행시킬 console.log(0) 을 실행시킨다.
(3) console.log(0)을 스택에서 제거하고, 다음 함수인 synchronous 함수를 실행.
(4) synchronous 함수에서 console.log(‘hello~’)를 실행.
(5) 모든 함수의 실행이 완료되었으니 스택에서 synchronous 파일을 제거.
4) 비동기방식 예제
{
console.log(0);
setTimeout(function() {
console.log('hello~');
}, 0);
console.log(1);
}
=> 결과값
[Running] node "/workspaces/codespaces-blank/index.js"
0
1
hello~
[Done] exited with code=0 in 0.05 seconds
코드의 실행과정
- 큐방식 (선입선출 (first in first out, FIFO) 구조)
(1) 먼저 스택에 실행하는 프로그램과 console.log(0) 을 넣어주고 실행시킨다.
(2) 실행이 완료됐으니 제거해 주고, setTimeout 함수를 가져온다. setTimeout은 Promise를 만들어주고 실행이 완료된다. 이 Promise는 큐 (Queue) 에 콜백 함수로써 들어가게 된다.
| |
| | ———————————————————————————————————– | ———————————————————————————————————— |
(3) 마지막으로 console.log(1)을 실행시켜준다.
| |
| | ———————————————————————————————————– | ———————————————————————————————————— |
(4) 모든 함수의 실행이 완료되었으니 프로그램을 스택에서 제거한다.
| |
| | ———————————————————————————————————– | ———————————————————————————————————— |
(5) 프로미스 (Promise) 로써 기다리고 있던 콜백함수가 이제 호출되어 실행된다.
| |
| | ———————————————————————————————————– | ———————————————————————————————————– |
(5) 프로미스 (Promise) 로써 기다리고 있던 콜백함수가 이제 호출되어 실행된다.
| |
| | ———————————————————————————————————– | ———————————————————————————————————– |
2. 비동기적 처리 방법(Asynchronous processing)
1) Callback
- 다른 함수가 실행을 끝낸 뒤 실행(call back)되는 함수
- 어떤 이벤트가 발생했거나 특정 시점에 도달했을 때 시스템에서 호출하는 함수
사용 이유
- 비동기적 프로그래밍 가능
- 싱글스레드를 사용하는데, 멈춤을 방지
/* 커피 주문 Callback */
{
setTimeout(()=>{
console.log("안녕하세요")
setTimeout(()=>{
console.log("아메리카노를 주문 했습니다")
setTimeout(()=>{
console.log("아메리카노")
setTimeout(()=>{
console.log("감사합니다")
},1000)
},1000)
},1000)
},1000)
}
=> 결과값
[Running] node "/workspaces/codespaces-blank/index.js"
안녕하세요
아메리카노를 주문 했습니다
아메리카노
감사합니다
[Done] exited with code=0 in 4.053 seconds
단점
- this를 사용한 콜백함수
- 무한콜백(Callback Hell) : 익명 함수로 전달하는 과정이 반복되어 코드의 들여쓰기 수준이 감당하기 힘들정도로 깊어지는 현상이다.(가독성 매우 떨어짐 ⇒ 코드 수정 어려움)
2) Promise
[Promise - JavaScript | MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise) |
동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냄
- 대기(pending): 이행하지도, 거부하지도 않은 초기 상태.
- 이행(fulfilled): 연산이 성공적으로 완료됨.
- 거부(rejected): 연산이 실패함.
- Promise 만들기
{
const promise = new Promise((resolve, reject) => {
/*
비동기 작업 성공시 resolve()를 호출하고,
비동기 작업 실패시 reject()를 호출하도록 구현한다.
*/
});
}
- Promise 성공 시 resolve를 호출하고 실패 시 reject를 호출한다.
{
const promise = new Promise((resolve, reject) => {
//처리 내용
});
promise
.then
//resolve가 호출되면 then이 실행
()
.catch
//reject가 호출되면 catch가 실행
()
.finally
//콜백 작업을 마치고 무조건 실행되는 finally (생략 가능)
();
}
- Promise 다음엔 then()과 catch()를 사용한다.
- then()은 생성한 프로미스 객체에서 인수로 전달한 resolve 가 호출되면 실행
- catch()는 생성한 프로미스 객체에서 인수로 전달한 resolve 가 호출되면 실행
/* 커피 주문 Promise */
{
const coffee = function(coffeeName){
return new Promise((resolve,reject)=>{ // resolve :성공 ,reject : 실패
if(coffeeName=="아메리카노"){
resolve("아메리카노를 주문 했습니다")
}else{
reject("해당 메뉴는 준비중입니다")
}
})
}
coffee("아메리카노")
.then(res=>console.log(res)) // 정상 실행은 resolve
.catch(rej=>console.log(rej)) // 실패는 reject
.finally(()=>console.log("감사합니다"))
}
=> 결과값
[Running] node "/workspaces/codespaces-blank/index.js"
아메리카노를 주문 했습니다
감사합니다
[Done] exited with code=0 in 0.089 seconds
사용 이유
- Callback 처리의 비동기 방식을 보완 (무한콜백에 따른 가독성)
단점
- 에러처리 (몇번째에서 발생했는지 알기 어려움)
- 무한 then() 발생 ^^
- 조건에 따른 분기를 나누기 힘듬
3) async/await
(1) async: 동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, 암시적으로 Promise를 사용하여 결과를 반환
[async function - JavaScript | MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/async_function) |
(2) await: Promise를 기다리기 위해 사용
[await - JavaScript | MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/await) |
- 함수 앞에 async - promiss가 된다
- await : .then()과 같다
단 await는 async안에서만 쓸 수 있음
- async/await 기본 문법
// ES5
async function 함수명() {
await 비동기 처리 메서드명();
}
// ES6
const 함수명 = async() => {
await 비동기 처리 메서드명();
}
/* 커피 주문 async/await */
{
const coffee = function(coffeeName){
return new Promise((resolve,reject)=>{ // resolve :성공 ,reject : 실패
if(coffeeName=="아메리카노"){
resolve("아메리카노를 주문 했습니다")
}else{
reject("해당 메뉴는 준비중입니다")
}
})
}
async function americano(){
try{
const result = await coffee("아메리카노")
console.log(result)
}catch(e){
console.log(e)
}finally{
console.log("감사합니다")
}
}
americano()
}
=> 결과값
[Running] node "/workspaces/codespaces-blank/index.js"
아메리카노를 주문 했습니다
감사합니다
[Done] exited with code=0 in 0.047 seconds
사용 이유
- Callback,Promise 처리의 비동기 방식을 보완 (무한콜백에 따른 가독성 보완 , 에러 처리)
유의
- 비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 함
- await를 사용하지 않으면 데이터를 받아온 시점에 콘솔을 출력할 수 있게 콜백 함수나 .then( ) 을 사용해야 한다.