특정 코드의 실행이 완료될 때까지 기다리고 난 후 다음 코드를 수행하는 것
을 의미특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드들을 수행하는 것
을 의미setTimeout(callback, millisecond)
setTimeout(function () {
console.log('1초 후 실행');
}, 1000);
// 123
clearTimeout(timerId)
setTimeout
타이머를 종료const timer = setTimeout(function () {
console.log('10초 후 실행');
}, 10000);
clearTimeout(timer);
// setTimeout이 종료됨.
setInterval(callback, millisecond)
setInterval(function () {
console.log('1초마다 실행');
}, 1000);
// 345
clearInterval(timerId)
setInterval
타이머를 종료const timer = setInterval(function () {
console.log('1초마다 실행');
}, 1000);
clearInterval(timer);
// setInterval이 종료됨.
Callback
Callback
함수를 활용하는 방법이 있다. 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', () => {});
});
});
};
printAll();
console.log(
`아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있습니다!`
);
// 출력 ↓
// 아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있습니다!
// A
// B
// C
Callback Hell
Callback
함수를 통해 비동기 코드의 순서를 제어할 수 있지만 코드가 길어질수록 복잡해지고 가독성이 낮아지는 Callback Hell이 발생하는 단점이 있다.
const printString = (string, callback) => {
setTimeout(function () {
console.log(string);
callback();
}, Math.floor(Math.random() * 100) + 1);
};
const printAll = () => {
printString('A', () => {
printString('B', () => {
printString('C', () => {
printString('D', () => {
printString('E', () => {
printString('F', () => {
printString('G', () => {
printString('H', () => {
printString('I', () => {
printString('J', () => {
printString('K', () => {
printString('L', () => {
printString('M', () => {
printString('N', () => {
printString('O', () => {
printString('P', () => {});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
};
printAll();
console.log(
`아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있지만 코드가 길어질 수록 복잡해지고 가독성이 낮아지는 Callback Hell이 발생하는 단점이 있습니다.`
);
// 출력 ↓
// 아래와 같이 Callback 함수를 통해 비동기 코드의 순서를 제어할 수 있지만 코드가 길어질 수록 복잡해지고 가독성이 낮아지는 Callback Hell이 발생하는 단점이 있습니다.
// A
// B
// ...
// O
// P (차례대로 호출)
Promise
new Promise
Promise
는 class이기 때문에 new
키워드를 통해 Promise
객체를 생성Promise
는 비동기 처리를 수행할 콜백 함수(executor
)를 인수로 전달받는데 이 콜백 함수는 resolve
, reject
함수를 인수로 전달받는다.Promise
객체가 생성되면 executor
는 자동으로 실행되고 작성했던 코드들이 작동.
코드가 정상적으로 처리가 되었다면 resolve
함수를 호출하고 에러가 발생했을 경우에는 reject
함수를 호출하면 된다.
let promise = new Promise((resolve, reject) => {
// 1. 정상적으로 처리되는 경우
// resolve의 인자에 값을 전달할 수도 있습니다.
resolve(value);
// 2. 에러가 발생하는 경우
// reject의 인자에 에러메세지를 전달할 수도 있습니다.
reject(error);
});
Promise 객체의 내부 프로퍼티
new Promise
가 반환하는 Promise
객체는 state
, result
내부 프로퍼티를 갖는다. .then
, .catch
, .finally
의 메서드를 사용해야 접근이 가능.pending
(대기)입니다. 비동기 처리를 수행할 콜백 함수(executor
)가 성공적으로 작동했다면 fulfilled
(이행)로 변경이 되고, 에러가 발생했다면 rejected
(거부)가 된다.undefined
입니다. 비동기 처리를 수행할 콜백 함수(executor
)가 성공적으로 작동하여 resolve(value)
가 호출되면 value
로, 에러가 발생하여 reject(error)
가 호출되면 error
로 변한다.then, catch, finally
Then
executor
에 작성했던 코드들이 정상적으로 처리가 되었다면 resolve
함수를 호출하고 .then
메서드로 접근할 수 있다. .then
안에서 리턴한 값이 Promise
면 Promise
의 내부 프로퍼티 result
를 다음 .then
의 콜백 함수의 인자로 받아오고, Promise
가 아니라면 리턴한 값을 .then
의 콜백 함수의 인자로 받아올 수 있다. 아래의 .then
과 Promise chaining
의 예시를 살펴보면서 동작 방식을 확인해 보자.
let promise = new Promise((resolve, reject) => {
resolve("성공");
});
promise.then(value => {
console.log(value);
// "성공"
})
Catch
executor에 작성했던 코드들이 에러가 발생했을 경우에는 reject 함수를 호출하고 .catch 메서드로 접근할 수 있다.
let promise = new Promise(function(resolve, reject) {
reject(new Error("에러"))
});
promise.catch(error => {
console.log(error);
// Error: 에러
})
Finally
executor
에 작성했던 코드들의 정상 처리 여부와 상관없이 .finally
메서드로 접근할 수 있다.
let promise = new Promise(function(resolve, reject) {
resolve("성공");
});
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('성공');
...
});
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()
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));
Promise
에서 executor
내 작성했던 코드들이 정상적으로 처리가 되었다면 결과를 배열에 저장해 새로운 Promise
를 반환.// 기존
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()
은 비동기 작업들을 동시에 처리.// promise.all
Promise.all([promiseOne(), promiseTwo(), promiseThree()])
.then((value) => console.log(value))
// ['1초', '2초', '3초']
.catch((err) => console.log(err));
Promise.all()
은 인자로 받는 배열에 있는 Promise
중 하나라도 에러가 발생하게 되면 나머지 Promise
의 state와 상관없이 즉시 종료.Error: 에러1
이 반환된 후로는 더 이상 작동하지 않고 종료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 Hell
Promise를 통해 비동기 코드의 순서를 제어할 수 있지만 Callback 함수와 같이 코드가 길어질수록 복잡해지고 가독성이 낮아지는 Promise Hell이 발생하는 단점이 있다.
const printString = (string) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(string);
}, Math.floor(Math.random() * 100) + 1);
});
};
const printAll = () => {
printString('A').then((value) => {
console.log(value);
printString('B').then((value) => {
console.log(value);
printString('C').then((value) => {
console.log(value);
printString('D').then((value) => {
console.log(value);
printString('E').then((value) => {
console.log(value);
printString('F').then((value) => {
console.log(value);
printString('G').then((value) => {
console.log(value);
printString('H').then((value) => {
console.log(value);
printString('I').then((value) => {
console.log(value);
printString('J').then((value) => {
console.log(value);
printString('K').then((value) => {
console.log(value);
printString('L').then((value) => {
console.log(value);
printString('M').then((value) => {
console.log(value);
printString('N').then((value) => {
console.log(value);
printString('O').then((value) => {
console.log(value);
printString('P').then((value) => {
console.log(value);
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
});
};
printAll();
console.log(
`아래와 같이 Promise를 통해 비동기 코드의 순서를 제어할 수 있지만 Callback 함수와 같이 코드가 길어질수록 복잡해지고 가독성이 낮아지는 Promise Hell이 발생하는 단점이 있습니다.`
);
// 출력 ↓
// 아래와 같이 Promise를 통해 비동기 코드의 순서를 제어할 수 있지만 Callback 함수와 같이 코드가 길어질수록 복잡해지고 가독성이 낮아지는 Promise Hell이 발생하는 단점이 있습니다.
// A
// B
// ...
// O
// P
Async/Await
async
/await
키워드를 제공하였다. Promise
코드를 간결하게 작성할 수 있게 되었다. async
키워드를 사용하고 async
함수 내에서만 await
키워드를 사용하면 됩니다. await
키워드가 작성된 코드가 동작하고 나서야 다음 순서의 코드가 동작하게 된다.// 함수 선언식
async function funcDeclarations() {
await 작성하고자 하는 코드
...
}
// 함수 표현식
const funcExpression = async function () {
await 작성하고자 하는 코드
...
}
// 화살표 함수
const ArrowFunc = async () => {
await 작성하고자 하는 코드
...
}
Node.js
브라우저에서 사용할 수 있는 비동기 흐름은 타이머 혹은 DOM 이벤트와 관련된 상황으로 다소 한정적이지만, Node.js의 경우 많은 API가 비동기로 작성되어 있다.
Node.js : 비동기 이벤트 기반 JavaScript 런타임
모듈이란?
- 건축으로부터 비롯된 모듈이라는 단어는, 어떤 기능을 조립할 수 있는 형태로 만든 부분
- 그중
fs
(File System) 모듈은, PC의 파일을 읽거나 저장하는 등의 일을 할 수 있게 도와준다.
DNS
: 파일 시스템 모듈이 파일을 읽거나 저장하는 기능을 구현할 수 있도록 돕는다.모듈을 사용하기 위해 불러오는 과정
이 필요하다.<script>
태그를 이용했다.// HTML에서 JavaScript 파일을 불러오는 script 태그
<script src="불러오고싶은_스크립트.js"></script>
require
구문을 이용하여 다른 파일을 불러온다.// Node.js에서 다른 파일을 불러오는 require 구문
const fs = require('fs'); // 파일 시스템 모듈을 불러옵니다
const dns = require('dns'); // DNS 모듈을 불러옵니다
예를 들어, Node.js에서 underscore는 Node.js 공식 문서에 없는 모듈이기 때문에 서드 파티 모듈입니다.
underscore
와 같은 서드 파티 모듈을 다운로드하기 위해서는 npm을 사용해야 합니다.underscore
: 내장 개체를 확장하지 않고 유용한 함수형 프로그래밍 도우미를 제공하는 JavaScript 라이브러리// 터미널에서 다음과 같이 입력해 underscore를 설치할 수 있습니다.
npm install underscore
// Node.js의 3rd-party underscore를 사용할 수 있습니다.
const _ = require('underscore');
fs.readFile
을 통해 알아보는 Node.js
공식문서 가이드fs.readFile
은 로컬에 존재하는 파일을 읽어온다.fs.readFile(path[, options], callback)
path \<string> | \<Buffer> | \<URL> | \<integer>
path
path
에는 파일 이름을 전달인자로 받습니다. <string>
)의 타입을 받습니다.// '/etc/passwd' 파일을 불러오는 예제
fs.readFile('/etc/passwd', ..., ...)
options \<Object> | \<string>
options
options
는 넣을 수도 있고, 넣지 않을 수도 있습니다. 대괄호는 선택적 전달인자를 의미한다.options는 문자열 또는 객체 형태로 받을 수 있다.
문자열로 전달할 경우 인코딩을 받는다.
밑의 예제에서는 'utf8'을 두 번째 전달인자로 받는 것을 확인할 수 있다.
// /etc/passwd 파일을 'utf8'을 사용하여 읽습니다.
// 두 번째 전달인자 options에 문자열을 전달한 경우
fs.readFile('/etc/passwd', 'utf8', ...);
// 두 번째 전달인자 options에 객체를 전달한 경우
let options = {
encoding: 'utf8', // utf8 인코딩 방식으로 엽니다
flag: 'r' // 읽기 위해 엽니다
}
// /etc/passwd 파일을 options를 사용하여 읽습니다.
fs.readFile('/etc/passwd', options, ...)
callback \<Function>
callback
err \<Error> | \<AggregateError>
data \<string> | \<Buffer>