TIL27, 비동기, Node.js 모듈
동기와 비동기
동기 (Synchronous)
- 작업 완료 시점과 작업 시작 시점이 같다
- 앞의 작업이 완료 돼야 다음 작업이 시작될 수 있다.
console.log('1st');
console.log('2nd');
console.log('3rd');
위 코드는 동기적인 실행을 거친다.
첫번째 코드가 먼저 실행되고, 그 실행이 끝나면 두번째 코드가 실행,
그 실행이 끝나면 세번째 코드가 실행되어 순서대로 '1st', '2nd', '3rd' 가 찍힌다.
앞의 코드의 실행이 끝날때까진 다음 코드가 실행될 수 없다.
이것이 동기 (Synchronous)이다.
비동기 (Asynchronous)
- 작업 완료 시점과 작업 시작이 같지 않아도 된다.
- 앞의 작업이 완료되지 않아도 다음 작업을 시작할 수 있다.
console.log('1st');
setTimeout (() => {
console.log('2nd');
}, 0);
console.log('3rd');
위 코드로 비동기 함수가 실행되는 메커니즘을 설명할 수 있다.
setTimeout() method를 이용해 지연시간을 0초로 줬으므로,
순서대로 '1st', '2nd', '3rd'가 찍힐것 같지만, 실제로는
'1st', '3rd', '2nd'가 찍히게 된다.
그 이유는, setTimeout이라는 함수가 비동기라는 것을 인지한 프로그램이
비동기 API를 처리하는 프로그램에 넘겨버린다.
그리고 작업을 처리하는 스택에 '1st' 가 쌓인 뒤 출력, '3rd'가 쌓인 뒤 출력되고,
스택의 모든 작업이 처리되되고 비워지면 API를 처리하는 프로그램에서 작업이 끝난 '2st'가
스택에 쌓인 뒤 출력된다.
동기와 비동기의 예
동기는 치킨집을 방문한 남훈이가 치킨을 주문하고, 그 치킨이 나올때 까지 뒤에있는 종서는 주문을 할 수 없다.
이를 blocking라고 부른다. 앞의 작업이 끝날 때까지 뒤의 작업들을 막는 것이다.
반면 비동기는, 남훈이가 치킨을 주문한 뒤 치킨이 나오면 받으러 오기로 하고, 뒤에있던 종서가 이어서 바로 주문을 한다. 앞의 작업이 끝날 때까지 blocking을 하지 않으므로, 효율적인 task 처리가 가능하다.
Callback
- 다른 함수의 매개변수로 들어갈 수 있는 함수.
- 어떤 이벤트가 실행됐을때 호출되는 함수
const test1 = (callback, num) => callback(num)
const test2 = (num) => num + 2;
console.log(test1(test2, 10)); // 12
위 코드처럼, test1의 첫번째 인자로 callback함수 test2, 두번째 인자로 num 10을 전달하고,
결국 test2(10)을 리턴한다. test2(10)을 실행하면 10 + 2 → 12가 결과로 나온다.
이것이 콜백함수이다.
하지만, 어떤 작업을 하다보면, callback이 쌓여 가독성에 아주 좋지 않은 코드가 될 때가 있다.
바로 위와 같은 경우이다.
이것을 Callback Hell이라고 한다. 말 그대로 콜백 지옥.
이 지옥에서 빠져나가기 위해 우리는 Promise라는 것을 알아야 한다.
Promise
- new Promise()를 사용한다
- new Promise()의 첫번째 인자는 resolve, 두번째 인자는 reject를 넣어준다.
- 성공적으로 불러왔다면 resolve() 의 값이 전달, ex) resolve(‘hi’)
- 문제가 생겼다면 reject()의 값이 전달된다. 이때 new Error을 사용. ex) reject(new Error(‘err..’))
// (3) test함수가 실행된다
const test = (num) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// (4) 인자로 전달받은 1을 찍는다.
console.log(num);
resolve();
// (5)1000ms (1초)의 간격.
}, 1000)
})
}
const testRun = () => {
// (2) test 함수에 인자를 1로 전달하고 실행한다.
test(1)
// (6) 위의 test(1) 함수의 실행이 끝나면, (.then)
.then(() => {
// (7) test 함수에 인자를 2로 전달하고 실행한다. 이후 반복.
return test(2)
})
// (8) 위의 test(2) 함수의 실행이 끝나면, (.then)
.then(() => {
// (9) test 함수에 인자를 3으로 전달하고 실행한다.
return test(3)
})
}
// (1) 제일 처음 testRun 함수 실행
testRun(); // (10) (1초 간격으로) 1, 2, 3
얘보다 더 깔끔하게 사용하기 위해서, Async await이라는걸 사용한다.
Async await
const test = (num) => {
return new Promise((resolve, reject) =>
setTimeout(() => {
console.log(num);
resolve();
}, 1000)
})
}
const testRun = async () => {
await test(1);
await test(2);
await test(3);
}
testRun(); // (1초 간격으로) 1, 2, 3
기존 promise 코드와 같지만, 호출하는 함수에서 함수명을 async로 지어주고,
일반 함수를 호출하는 것처럼 호출하면 된다. 단, 앞에 await을 붙여준다.
async와 await은 짝궁이다.
타이머 API
setTimeout(callback, millisecond)
- 일정 시간 (두번째 인자 millisecond) 후에 함수를 실행한다.
- 인자 : 실행할 callback 함수, callback함수 실행 전 기다릴 시간을 전달
- 리턴 : 임의의 타이머 ID
setTimeout(() => { console.log('1초 후에 실행됩니다'); }, 1000); // (1초 뒤) 1초 후에 실행됩니다
setInterval(callback, millisecond)
- 일정 시간 (두번째 인자 millisecond)을 가지고 함수를 반복적으로 실행한다.
- 인자 : 실행할 callback 함수,반복적으로 함수를 실행시키기 위한 시간 간격을 전달
- 리턴 : 임의의 타이머 ID
setInterval(() => { console.log('1초마다 실행됩니다'); }, 1000); // (1초마다) 1초마다 실행됩니다
clearInterval(timerId)
- 반복중인 타이머를 종료한다.
- 인자 : 타이머 ID
- 리턴 : 리턴하지 않음
// 위에서 1초마다 실행되는 타이머의 아이디가 1일때 clearInterval(1) // 그녀석의 반복을 중단한다.
Node.js 모듈
- 비동기 이벤트 기반 자바스크립트 런타임.
- 로컬 환경에서 자바스크립트를 실행할 수 있다.
- 브라우저에서 불가능한 몇가지 일들이 가능하다.
Node..js 모듈 불러오기
→ 코드 가장 상단에 requre를 이용해 불러온다.
const fs = require('fs') // file system 모듈을 불러온다
const dns = require('dns') // DNS 모듈을 불러온다
3rd-party 모듈 (써드 파티 모듈) 사용하기
→ 얼마전 공부했던 underscore (우리에겐 underbar)는 Node.js에서 공식적으로 제공하는 빌트인 모듈이 아닌, 써드파이 모듈이다. 이런 모듈들을 사용하기 위해서는 npm을 이용해야한다.
npm install underscore
를 입력해 underscore를 다운받고, 위에서 했던 방식대로 모듈을 불러오면 된다.
const _ = require('underscore')