출처: 프론트엔드 3주차 수업 (Core Javascript 1)
현재 실행 중인 코드가 끝나야 다음 코드를 실행
= 현재 실행 중인 task가 종료할 때까지 다음 task는 대기
const arr = [];
for (let i = 0; i < 1000; i++) {
arr.push(i); // (1000번 실행)
}
alert("안녕하세요"); // 확인을 누를 때까지 console 코드로 넘어가지 않음
console.log(arr);
현재 실행 중인 코드가 완료되지 않아도
나중에 실행하라고 브라우저에 맡겨놓고 다음 코드로 넘어감
// 코드 1
axios.get(url)
.then(response => {
// api 호출하고 응답받으면 실행
})
// 코드 2
api 호출하고 응답은 언제 올지 모름
장점: blocking(작업 중단)이 발생하지 않는다.
단점: 코드 실행 순서가 보장되지 않는다.
자바스크립트 엔진이 싱글 스레드이기 때문이다.
싱글 스레드 (Single Thread)
호출 스택이 하나여서 한 번에 하나의 task만 실행할 수 있는 시스템
(처리에 시간이 걸리는 작업을 실행하면 blocking이 발생한다)
=> 콜백함수의 등장으로 자바스크립트에서도
서로 실행 시점이 다른 비동기를 처리할 수 있게 되었다.
주의점: 콜백함수는 동기에서도 쓰이는 개념이다.
cf. 고차 컴포넌트 Higher Order Component(HOC)
: 컴포넌트를 매개변수로 받아서 컴포넌트를 반환하는 컴포넌트
이렇게 함수의 인수로 함수를 전달할 수 있는 것은,
자바스크립트가 일급함수를 가질 수 있는 함수형 프로그래밍 언어이기 때문이다.
변수처럼 기능을 하는 함수
- 함수를 다른 함수에 인수로 제공할 수 있다.
- 리턴문에 함수를 반환할 수 있다.
- 변수에 함수를 할당할 수 있다.
특정 이벤트 발생 시
일정 시간 경과 후
서버에 통신 요청 시
AJAX(Asynchronous JavaScript and XML)
브라우저가 서버에게 자바스크립트를 사용하여
비동기 방식으로 데이터를 요청하고, 응답한 데이터를 수신하여
웹페이지 일부를 동적으로 갱신하는 방식
다음과 같이 api에 필요한 정보가 한꺼번에 담겨서 오면
프론트엔드 입장에서는 편하다. (api 호출 한번으로 필요한 데이터를 한번에 꺼낼 수 있어서)
[{
"id": 1,
"name": "운동화",
"price": 30000
"comments": [{
"comment": "강추합니다",
"username": "Kim",
"likes": [{
"like": true,
"username": "lee"
}]
}]
}]
하지만 만약 3개의 api로 나뉘어 와 버렸다면?
[{
"id": 1,
"name": "운동화",
"price": 30000
}]
[{
"comment": "강추합니다",
"username": "Kim"
}]
[{
"like": true,
"username": "lee"
}]
$.get('https://api.test.com/proudcts', function(response) {
var firstProductId = response.products[0].id;
$.get('https://api.test.com/proudct/comments?id='+firstProductId, function(response) {
var firstCommentId = response.comments[0].id;
$.get('https://api.test.com/proudct/comment/likes?id='+firstCommentId, function(response) {
var likes = response.likes;
var likesCount = likes.length;
});
});
});
let promise = new Promise()
) let promise
)로 원하는 비동기 동작을 처리한다.let promise = new Promise(function(resolve, reject) {
// 비동기 로직 작성
});
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
// resolve 함수에 인자를 넘기면
resolve('hello world');
}, 2000);
});
promise.then(function(msg) {
console.log(msg); // 여기서 실행된다 (2초 뒤에 hello world 출력)
});
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
// reject가 실행되면
reject('실패');
}, 2000);
});
promise.then(function() {
console.log('resolve'); // 여기는 넘어가고
}, function(msg) {
console.log('reject', msg); // 여기만 실행됨 ('reject 실패' 출력)
});
then으로 resolve, reject를 다 받는 것보다
catch가 가독성이 더 좋다.
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
reject('실패');
}, 2000);
});
promise
// 위에처럼 then에 ','로 두 개를 연결하지 않고
.then(function() {}) // resolve를 받는 then과
.catch(function(err) { // reject를 받는 catch로 나누었다.
console.log(err);
});
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(1);
resolve(2);
}, 1000);
});
promise.then(function(msg) {
console.log(msg);
});
(답 확인: F12 - console에 코드 복붙)
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log(1);
resolve(2);
console.log(3);
resolve(4);
}, 1000);
});
promise.then(function(data) {
console.log(data);
});
let promise = new Promise(function(resolve, reject) {
resolve(1);
setTimeout(function () {
resolve(2);
}, 1000);
});
promise.then(function(data) {
console.log(data);
});
주의: 중간에 catch 있음 + 성공 시 chaining 실행
function job() {
return new Promise(function(resolve, reject) {
reject();
});
}
job()
.then(function() {
console.log(1);
})
.then(function() {
console.log(2);
})
.then(function() {
console.log(3);
})
.catch(function() {
console.log(4);
})
.then(function() {
console.log(5);
});
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('before', promise)
resolve(1);
console.log('after', promise);
}, 1000);
});
promise.then(function(data) {
console.log(data);
});
let promise = new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('before', promise)
reject(1);
console.log('after', promise);
}, 1000);
});
promise.then(function(data) {
console.log('resolve', data);
}, function (data) {
console.log('reject', data);
});
```jsx
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 9000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(function(value) {
console.log(value);
});
```
- 결과: 9초 뒤에 `[1, 2, 3]` 출력
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
let requests = urls.map(url => fetch(url));
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));
(async function() {
// 내용
})();
// 또는 function 없이
(async () => {
// 내용
})();
axios('https://api.test.com/proudcts')
.then(function(response) {
let firstProductId = response.products[0].id;
return axios('https://api.test.com/proudct/comments?id='+firstProductId);
})
.then(function(response) {
let firstCommentId = response.comments[0].id;
return axios('https://api.test.com/proudct/comment/likes?id='+firstCommentId)
})
.then(function(response) {
let likes = response.likes;
let likesCount = likes.length;
});
위의 코드를 async/await + try/catch + 즉시실행함수 조합으로 바꾸면
(async () => {
try {
let productResponse = await fetch('https://api.test.com/proudcts');
let firstProductId = productResponse.products[0].id;
let commentResponse = await fetch('https://api.test.com/proudct/comments?id='+firstProductId);
let firstCommentId = commentResponse.comments[0].id;
let likeResponse = await fetch('https://api.test.com/proudct/comment/likes?id='+firstCommentId);
let likesCount = likeResponse.likes.length;
} catch(error) {
console.log(error);
}
})();
이벤트루프에 대한 자세한 내용은 이전 포스트 참고 👉https://velog.io/@yena1025/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EB%9E%80
const foo = () => console.log("First");
const bar = () => setTimeout(() => console.log("Second"), 500);
const baz = () => console.log("Third");
bar();
foo();
baz();