동기(Synchronous) 코드는 작성된 순서대로 실행되며, 하나의 작업이 끝나기 전에는 다음 작업이 시작되지 않는다.
console.log(1)
console.log(2)
alert('확인!')
console.log(3)
console.time('Loop!')
for (let i = 0; i < 1000000000; i++) {}
console.timeEnd('Loop!')
console.log(4)
비동기(Asynchronous) 코드는 작성된 순서대로 실행되지만, 특정 작업이 끝나기 전에 다음 작업이 시작될 수 있다.
✍ 예제 1
console.log(1)
console.log(2)
console.log(3)
console.time('Loop!')
setTimeout(() => {
for (let i = 0; i < 1000000000; i++) {}
console.timeEnd('Loop!')
console.log(5)
}, 0) // 대표적인 비동기 코드
console.log(4)
✍ 예제 2
console.log(1)
const h1El = document.querySelector('h1')
h1El.addEventListener('click', () => {
console.log('클릭!')
})
console.log(2)
1,2 는 먼저 뜨고 h1을 누르면 클릭! 이 나옴
✍ 예제 3
console.log(1)
fetch('http://api.heropy.dev/v0/users')
.then(res => res.json)
.then(data => console.log(data))
console.log(2)
✍ 예제 4
const h1El = document.querySelector('h1')
h1El.textContent = 'Loading'
const timer = setInterval(() => {
h1El.textContent += '.'
}, 500)
const imgEl = document.createElement('img')
imgEl.src = 'https://picsum.photos/3000/2000'
imgEl.addEventListener('load',() => {
document.body.append(imgEl)
clearInterval(timer)
h1El.textContent = 'Done!'
})
✍ 예제 1
// timer.js
export function timer(callback) {
setTimeout(() => {
console.log(1)
callback()
}, 2000)
}
// main.js
import { timer } from "./timer.js"
timer(() => {
console.log(2)
})
📍 2초 뒤에 1이 뜨고 2가 뜸
✍ 예제 2
function renderImage(callback) {
const imgEl = document.createElement('img')
imgEl.src = 'http://picsum.photos/500/500'
imgEl.addEventListener('load', () => {
document.body.append(imgEl)
callback()
})
}
renderImage(() => {
console.log('Done!')
})
📍 사진이 화면에 출력되고 나서 콘솔창에 Done!이 뜸
function renderImage(callback) {
const imgEl = document.createElement('img')
imgEl.src = 'http://picsum.photos/500/500'
imgEl.addEventListener('load', () => {
document.body.append(imgEl)
callback()
})
}
renderImage(() => {
console.log('Done 1')
})
renderImage(() => {
console.log('Done 2')
})
renderImage(() => {
console.log('Done 3')
})
renderImage(() => {
console.log('Done 4')
})
새로고침을 하면 계속 바뀜
function renderImage(callback) {
const imgEl = document.createElement('img')
imgEl.src = 'http://picsum.photos/500/500'
imgEl.addEventListener('load', () => {
document.body.append(imgEl)
callback()
})
}
renderImage(() => {
console.log('Done 1')
renderImage(() => {
console.log('Done 2')
renderImage(() => {
console.log('Done 3')
renderImage(() => {
console.log('Done 4')
})
})
})
})
promise 클래스를 사용함promise 클래스 사용function renderImage() {
return new Promise((callback) => {
const imgEl = document.createElement('img')
imgEl.src = 'http://picsum.photos/500/500'
imgEl.addEventListener('load', () => {
document.body.append(imgEl)
callback()
})
})
}
renderImage()
.then(() => {
console.log('Done 1')
return renderImage()
})
.then(() => {
console.log('Done 2')
return renderImage()
})
.then(() => {
console.log('Done 3')
return renderImage()
})
.then(() => {
console.log('Done 4')
return renderImage()
})
const myPromise = new Promise(() => {
// 비동기 작업 처리~!
})
✍ 예제 1
/**
* Promise : 비동기 작업을 처리하는 객체!
* Promise 객체는 new 키워드와 생성자 함수를 통해 생성할 수 있다.
* 생성할 때는 인자로 콜백함수를 넣을 수 있다.
* 콜백함수 안에는 비동기 코드를 작성
* resolve: 정상적인 결과 값을 반환 (이행) - 쉽게 말해 return대신 쓰였다고 생각하면 됨
* reject: 정상적이지 않았던 값을 반환 (거부)
*/
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const text = prompt('"hello"를 입력하면 선물을 드립니다!')
if (text === 'hello') {
resolve('💻')
} else {
reject('error message!')
}
}, 2000)
})
/**
* Promise는 상태를 가지고있음
* - 대기 (pending): 비동기 작업을 처리하는 중..
* - 이행 (fulfilled): 비동기 작업이 정상적으로 처리가 된 경우!
* - 거부 (rejected): 비동기 작업이 정상적으로 처리되지 않은 경우!
*
* 메소드
* - then(): 이행되었을 때
* - catch(): 거부되었을 떄
* - finally(): 이행되거나 거부되더라도 항상!
*/
//약속의 결과
myPromise
.then((result) => {
console.log('result: ', result)
})
.catch((err) => {
console.log('error: ', err)
})
.finally(() => {
console.log('complete!')
})
//resolve 에 넣었던 결과값이 then의 파라미터로 떨어지고
//reject 에 넣었던 결과값이 catch의 파라미터로 떨어진다
✍ 예제 2
바꾸기 전
function timer(callback) {
setTimeout(() => {
console.log(1)
callback('1 is Done!')
}, 2000)
}
timer((msg) => {
console.log(msg)
console.log(2)
})
바꾼 후
function timer() {
return new Promise(resolve => {
setTimeout(() => {
console.log(1)
resolve('1 is Done!')
}, 2000)
})
}
timer()
.then(msg => {
console.log(msg)
console.log(2)
return timer() // 다음 then 메소드를 사용하려면 promise 인스턴스를 반환해야한다
})
.then(msg => {
console.log(msg)
console.log(2)
return timer()
})
: Promise 객체에 연속적으로 메소드를 호출하는 것
myPromise
.then((result) => {
console.log('result: ', result)
return `선물은 : ${result}` // 두번째 결과의 파라미터 즉, 아래의 result로 들어감
})
.then((result) => {
console.log('result: ', result)
})
.catch((err) => {
console.log('error: ', err)
})
.finally(() => {
console.log('complete!')
})
바꾸기 전
/* html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<;title>Document</title>
<script defer src="./main.js"></script>
<style>
.users {
padding: 0;
}
.users li {
height: 30px;
display: flex;
align-items: center;
gap: 10px;
}
.users li img {
width: 24px;
height: 24px;
border-radius: 50%;
}
.users li.no-photo img {
filter: grayscale(100%) opacity(50%)
}
</style>
</head>
<body>
<h1>Hello world!</h1>
</body>
</html>
/* javascript */
const h1El = document.querySelector('h1')
const ulEl = document.createElement('ul')
ulEl.classList.add('users')
document.body.append(ulEl)
h1El.addEventListener('click', () => {
ulEl.textContent = 'Loading...'
fetch('http://api.heropy.dev/v0/users')
.then(res => res.json())
.then(data => {
console.log(data)
const { users } = data // 객체 구조 분해 할당
const liEls = users.map(user => {
const liEl = document.createElement('li')
liEl.textContent = user.name
const imgEl = document.createElement('img')
imgEl.src = user.photo?.url || 'http://heropy.dev/favicon.png'
if (!user.photo) {
liEl.classList.add('no-photo')
}
liEl.prepend(imgEl)
return liEl
})
ulEl.textContent = ''
ulEl.append(...liEls)
})
})
바꾼 후
const h1El = document.querySelector('h1')
const ulEl = document.createElement('ul')
ulEl.classList.add('users')
document.body.append(ulEl)
h1El.addEventListener('click', async () => {
ulEl.textContent = 'Loading...'
const res = await fetch('http://api.heropy.dev/v0/users')
// 패치 함수가 호출되는 것을 기다리게 만들어줌
const data = await res.json()
console.log(data)
const { users } = data // 객체구조분해할당
const liEls = users.map(user => {
const liEl = document.createElement('li')
liEl.textContent = user.name
const imgEl = document.createElement('img')
imgEl.src = user.photo?.url || 'http://heropy.dev/favicon.png'
if (!user.photo) {
liEl.classList.add('no-photo')
}
liEl.prepend(imgEl)
return liEl
})
ulEl.textContent = ''
ulEl.append(...liEls)
})