λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ“Œ Language/Javascript

[JS] 비동기 μ²˜λ¦¬ν•˜λŠ” 법 μ•Œμ•„λ³΄κΈ°(Callback,Promise,Async & Await)

by Fomagran πŸ’» 2022. 2. 15.
728x90
λ°˜μ‘ν˜•

μ•ˆλ…•ν•˜μ„Έμš” FomaπŸ’» μž…λ‹ˆλ‹€.

 

μ˜€λŠ˜λ„ λͺ¨λ˜ μžλ°”μŠ€ν¬λ¦½νŠΈλ₯Ό 톡해 배운 Callback ν•¨μˆ˜μ™€ Promise에 λŒ€ν•΄μ„œ μ œκ°€ μ΄ν•΄ν•œ λ°©μ‹λŒ€λ‘œ

 

정리해보도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

λ°”λ‘œ μ‹œμž‘ν• κ²Œμš”~


Callback

 

Callback ν•¨μˆ˜λŠ” μ‹€ν–‰ν•˜λŠ” μ¦‰μ‹œ μ™„λ£Œλ˜λŠ” 것이 μ•„λ‹ˆλΌ νŠΉμ • μž‘μ—…μ„ μ‹€ν–‰ν•˜κ³  마친 뒀에 μ™„λ£Œλœλ‹€.

 

즉, λΉ„λ™κΈ°μ μœΌλ‘œ μ²˜λ¦¬λ˜λŠ” 것을 μ˜λ―Έν•œλ‹€.

 

λ§Œμ•½ 슀크립트λ₯Ό λ§Œλ“œλŠ” μž‘μ—…μ΄ μžˆλ‹€λ©΄ μž‘μ—…μ΄ μ •μƒμ μœΌλ‘œ μ²˜λ¦¬λ˜κ±°λ‚˜ μ€‘κ°„μ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•˜λŠ” κ²½μš°μ— μž‘μ—…μ΄ μ™„λ£Œλœλ‹€.

 

콜백 ν•¨μˆ˜κ°€ μ™„λ£Œλ˜λŠ” κ²½μš°λŠ” μ •μƒμ μœΌλ‘œ μ²˜λ¦¬λ˜μ—ˆκ±°λ‚˜, μ—λŸ¬κ°€ λ°œμƒν•œ 경우 뿐이닀.

 

콜백 ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜λŠ” 방식은 μ›ν•˜λŠ” νŒŒλΌλ―Έν„° κ°’κ³Ό ν•¨κ»˜ μž‘μ—…μ„ μ‹€ν–‰ν•  ν•¨μˆ˜λ₯Ό λ°˜λ“œμ‹œ 같이 λ„£μ–΄μ€€λ‹€.

 

function loadScript(src,callback) {
    let script = document.createElement('script')
    script.src = src
    //μ •μƒμ μœΌλ‘œ μ²˜λ¦¬λμ„ 경우
    script.onload = () => callback(null,script)
    //μ—λŸ¬κ°€ λ°œμƒν–ˆμ„ 경우
    script.onerror = () => callback(new Error(`${src}λ₯Ό λΆˆλŸ¬μ˜€λŠ” 도쀑에 μ—λŸ¬κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.`))
    document.head.append(script)
}

//μ½œλ°±ν•¨μˆ˜ μ‹€ν–‰
loadScript('/javascript/syntax.js',function(error,script) {
    if (error) {
        //μ—λŸ¬κ°€ μžˆλ‹€λ©΄ μ—λŸ¬ 처리
    }else {
        //μ—†λ‹€λ©΄ 정상싀행
    }
})

Pyramid of doom

 

Pyramid of doom은 멸망의 ν”ΌλΌλ―Έλ“œλΌκ³ λ„ ν•˜λŠ”λ°, 이것은 μ²˜λ¦¬ν•΄μ•Όν•  콜백 ν•¨μˆ˜κ°€ μ—¬λŸ¬κ°œμΈ κ²½μš°μ— λ°œμƒν•œλ‹€.

 

콜백 ν•¨μˆ˜ μ•ˆμ— 콜백 ν•¨μˆ˜κ°€ 이어 μžˆλŠ” κ²½μš°μ΄λ―€λ‘œ μ½”λ“œκ°€ κ°€λ‘œ,μ„Έλ‘œ λͺ¨λ‘ 길게 이어진닀.

 

이것은 μ½”λ“œμ˜ 가독성이 μ•ˆμ’‹μ•„μ§€κΈ° λ•Œλ¬Έμ— 콜백 ν•¨μˆ˜μ˜ 큰 단점이닀.

 

loadScript('1.js', function(error, script) {
    if (error) {
      handleError(error);
    } else {
      // ...
      loadScript('2.js', function(error, script) {
        if (error) {
          handleError(error)
        } else {
          // ...
          loadScript('3.js', function(error, script) {
            if (error) {
              handleError(error)
            } else {
              // λͺ¨λ“  μŠ€ν¬λ¦½νŠΈκ°€ λ‘œλ”©λœ ν›„, μ‹€ν–‰ 흐름이 μ΄μ–΄μ§‘λ‹ˆλ‹€. (*)
            }
          })
  
        }
      })
    }
  })

 

μ•„λž˜μ™€ 같이 λ‚˜λˆ μ“°λ©΄ μœ„μ˜ μ½”λ“œλ₯Ό μ™„ν™”ν•  순 μžˆμ§€λ§Œ, 이것 λ˜ν•œ μ‹€ν–‰ν•΄μ•Ό ν•  슀크립트만큼 μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ•Ό ν•˜κ³  μž¬μ‚¬μš©μ΄ λΆˆκ°€ν•˜λ‹€.

 

function step1(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('2.js', step2);
  }
}

function step2(error, script) {
  if (error) {
    handleError(error);
  } else {
    // ...
    loadScript('3.js', step3);
  }
}

function step3(error, script) {
  if (error) {
    handleError(error);
  } else {
    // λͺ¨λ“  μŠ€ν¬λ¦½νŠΈκ°€ λ‘œλ”©λ˜λ©΄ λ‹€λ₯Έ λ™μž‘μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. (*)
  }
};

Promise

 

PromiseλŠ” μœ„μ™€ 같은 콜백 ν•¨μˆ˜μ˜ 단점을 λŒ€μ²΄ν•  수 μžˆλŠ” 객체이닀.

 

μƒμ„±ν•˜λŠ” 방법은 μ•„λž˜μ™€ 같이 성곡(resolve)ν–ˆμ„ λ•Œμ™€ μ‹€νŒ¨(reject)ν–ˆμ„ λ•Œλ₯Ό νŒŒλΌλ―Έν„°λ‘œ 생성할 λ•Œ 같이 λ„£μ–΄μ€λ‹ˆλ‹€.

 

그리고 μ„±κ³΅ν–ˆμ„ λ•ŒλŠ” resolove(μ›ν•˜λŠ” κ°’), μ—λŸ¬κ°€ λ°œμƒν–ˆμ„ 땐 reject(μ—λŸ¬)둜 값을 λ°˜ν™˜ν•œλ‹€.

 

let promise = new Promise(function(resolve,reject) {
    //λ§Œμ•½ μ—λŸ¬κ°€ μ—†λ‹€λ©΄
    resolve("성곡")
    //μ—λŸ¬κ°€ λ°œμƒν–ˆλ‹€λ©΄
    reject(new Error("μ—λŸ¬λ°œμƒ"))
})

 

μœ„μ—μ„œ λ§Œλ“  promise 객체λ₯Ό κ΅¬λ…ν•˜μ—¬ 값이 λ°˜ν™˜λμ„ λ•Œ λ°›μ•„μ˜¬ 수 μžˆλŠ”λ°, 이것은 .then으둜 μž‘μ„±ν•œλ‹€.

 

promise.then(
    //κ²°κ³Όκ°€ μ—λŸ¬κ°€ μ—†μœΌλ©΄
    result => console.log(result),
    //κ²°κ³Όκ°€ μ—λŸ¬κ°€ μžˆλ‹€λ©΄
    error => console.log(error)
)

 

μ„±κ³΅ν–ˆμ„ λ•Œλ§Œ λ°›μ•„μ˜€κ³  μ‹Άλ‹€λ©΄ μ•„λž˜μ™€ 같이 성곡에 λŒ€ν•œ μž‘μ—…λ§Œ μ²˜λ¦¬ν•΄μ£Όκ³ ,

 

promise.then(result => console.log(result))

 

μ‹€νŒ¨ν–ˆμ„ λ•Œλ§Œ λ°›μ•„μ˜€κ³  μ‹Άλ‹€λ©΄  .catchλ₯Ό μ΄μš©ν•΄μ„œ μ—λŸ¬λ₯Ό 핸듀링 ν•  수 μžˆλ‹€.

 

promise.catch(error => console.log(error))

 

λ˜ν•œ μ‹€νŒ¨ν•˜λ“  μ„±κ³΅ν•˜λ“  무쑰건 μ²˜λ¦¬λ˜λŠ” μ–΄λ– ν•œ μž‘μ—…μ΄ μžˆλ‹€λ©΄ finallyλ₯Ό 톡해 μ²˜λ¦¬ν•΄μ€€λ‹€.

 

promise.finally(
    console.log("Finally 무쑰건 싀행됨.")
).then(result => console.log(result))
.catch(error => console.log(error))

 

μœ„ 콜백 ν•¨μˆ˜ loadScriptλ₯Ό Promise둜 λ‹€μ‹œ μž‘μ„±ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

 

function loadScript(src) {
    return new Promise(function(resolve,reject) {
        let script = document.createElement('script')
        script.src = src
        script.onload = () => resolve(script)
        script.onerror = () => reject(new Error(`${src}λ₯Ό λΆˆλŸ¬μ˜€λŠ” 도쀑 μ—λŸ¬λ°œμƒ`))
        document.head.append(script)
    })
}

 

콜백 ν•¨μˆ˜μ˜ 큰 λ‹¨μ μ΄μ—ˆλ˜ 멸망의 ν”ΌλΌλ―Έλ“œλ₯Ό μ•„λž˜μ™€ 같이 짧고, 가독성 있게 μž‘μ„±ν•  수 있게 λœλ‹€.

 

fetch("https://www.naver.com")
.then(script => fetch("https://www.naver.com"))
.then(script => fetch("https://www.google.com"))
.then(script => {
    console.log(script)
})

Promise API

 

이 외에도  Promise의 μž₯점은 μ—¬λŸ¬κ°€μ§€ APIλ₯Ό 가지고 μžˆλ‹€λŠ” 것이닀.

 

Promise.all

 

μ—¬λŸ¬ ν”„λ‘œλ―ΈμŠ€λ₯Ό ν•œλ²ˆμ— λ‹΄μ•„μ„œ μ²˜λ¦¬ν•  수 μžˆλ‹€.

 

단, ν•˜λ‚˜λΌλ„ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€λ©΄ κ·Έ μ¦‰μ‹œ μ€‘λ‹¨ν•˜κ³  μ—λŸ¬λ₯Ό λ°˜ν™˜ν•œλ‹€.

 

 Promise.all([
    new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
    new Promise((resolve, reject) => setTimeout(() => reject(new Error("μ—λŸ¬ λ°œμƒ!")), 2000)),
    new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
  ]).catch(alert); // Error: μ—λŸ¬ λ°œμƒ!

 

Promise.allSettled

 

Promise.allκ³Ό 같이 μ—¬λŸ¬ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ‹΄μ•„ ν•œλ²ˆμ— μ²˜λ¦¬ν•˜λŠ”λ°, 

 

μž‘μ—…μ΄ μ™„λ£Œλ˜μ—ˆμœΌλ©΄ fullfilled둜 μ—λŸ¬κ°€ λ°œμƒν–ˆμœΌλ©΄ reject둜 λͺ¨λ‘ λ°˜ν™˜λœλ‹€.

 

 Promise.allSettled([
    new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
    new Promise((resolve, reject) => setTimeout(() => reject(new Error("μ—λŸ¬ λ°œμƒ!")), 2000)),
    new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
  ]).then(results => {
      results.forEach((result, num) => {
        if (result.status == "fulfilled") {
          console.log(`${result.value.status}`)
        }
        if (result.status == "rejected") {
          console.log(`${result.reason}`)
        }
      })
      
/*
{status: 'fulfilled'.},
{status: 'fulfilled'},
{status: 'rejected'}
*/

 

Promise.race

 

μœ„μ˜ 두 API와 같이 λͺ¨λ‘ ν•œλ²ˆμ— λ‹΄μ•„μ„œ μ²˜λ¦¬ν•˜μ§€λ§Œ κ°€μž₯ 처리 속도가 λΉ λ₯Έ κ²ƒμ˜ μž‘μ—…κ²°κ³Όλ₯Ό λ°˜ν™˜ν•œλ‹€.

 

Promise.race([
    new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
    new Promise((resolve, reject) => setTimeout(() => reject(new Error("μ—λŸ¬ λ°œμƒ!")), 2000)),
    new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
  ]).then(result => console.log(result)); // 1

Async & Await

 

Async와 Awaitλ₯Ό μ‚¬μš©ν•˜λ©΄ Promiseλ₯Ό μ’€ 더 νŽΈν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€.

 

λΉ„λ™κΈ°μ μœΌλ‘œ μ²˜λ¦¬λ˜λŠ” μ–΄λ–€ ν•¨μˆ˜κ°€ μžˆλ‹€λ©΄ μ•žμ— asyncλ₯Ό λΆ™μ—¬μ€€λ‹€.

 

κ·Έ λ‹€μŒ ν•΄λ‹Ή ν•¨μˆ˜λ₯Ό Promise와 λ™μΌν•˜κ²Œ κ΅¬λ…ν•΄μ„œ κ²°κ³Όλ₯Ό λ°›μ•„μ˜¨λ‹€.

 

async function f() {
    return 1
}
f().then(result => console.log(result))

 

λ˜λŠ” Promiseλ₯Ό μƒμ„±ν•΄μ„œ 결과값을 await을 톡해 κ°€μ Έμ˜¬ μˆ˜λ„ μžˆλ‹€.

 

async function f(){
    let promise = new Promise((resolve,reject) => {
        setTimeout(() => resolve("μ™„λ£Œ"),1000)
    })

    let result = await promise
    console.log(result)
}

 

κΉƒν—™μ—μ„œ 아바타λ₯Ό κ°€μ Έμ˜€λŠ” 과정을 async&awaitκ³Ό Promiseλ₯Ό μ‚¬μš©ν•΄μ„œ λ‚˜νƒ€λ‚΄λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

 

async function showAvatar(url) {
    let response = await fetch(url)
    let user = await response.json()

    let githubResponse = await fetch(`https://api.github.com/users/${user.name}`)
    let githubUser = await githubResponse.json()

    let img = document.createElement('img')
    img.src = githubUser.avatar._url
    img.className = 'promise-avatar-example'
    document.body.append(img)

    await new Promise((resolve,reject) => setTimeout(resolve,3000))
    img.remove()
    return githubUser
}

Reference

 

 

λͺ¨λ˜ JavaScript νŠœν† λ¦¬μ–Ό

 

ko.javascript.info

 

728x90
λ°˜μ‘ν˜•

λŒ“κΈ€