200427 TIL

kwd8905·2021년 4월 27일
0

비동기적 실행

자바스크립트에는 동작을 원하는 시점에 동작하게 만드는 여러 가지 방법이 주어진다. 아래 함수는 문자열을 인자로 받아 문자열을 콘솔에 출력해주는 아주 기본적인 코드이다.

const printString = (string) => {
  console.log(string)
}

이 함수를 printAll이라는 함수 안에 넣어서 실행을 시키면 어떻게 될까?

const printAll = () => {
  printString('A')
  printString('B')
  printString('C')
}

printAll();
//A
//B
//C

이미 잘 알고 있는 것처럼 전달받은 A, B, C가 차례로 출력된다. 이렇게 실행되는 것을 동기적 실행이라고 한다. 여기에 setTimeout 메소드를 이용하여 비동기적인 실행을 할 수 있도록 해보자.

const printString = (string) => {
  setTimeout(
    () => {
      console.log(string)
    },
    Math.floor(Math.random() * 100) + 1
  )
}

const printAll = () => {
  printString('A')
  printString('B')
  printString('C')
}

printAll();
//ABC //BAC //CBA...

setTimeout으로 함수가 실행되는 시간을 무작위(random)으로 주고 실행을 시켜보았다. 결과는 재미있게도, A, B, C가 순서와 상관없이 나오는 것을 확인할 수 있다. 이렇게 setTimeout을 이용하면 함수가 순차적으로 실행되지 않고 지정한 시간에 맞추어 실행되게 만들 수 있다.

Callback

이번에는 콜백함수를 추가해 본다.

const printString = (string, callback) => {
  setTimeout(
    () => {
      console.log(string)
      callback()
    },
    Math.floor(Math.random() * 100) + 1
  )
}

const printAll = () => {
  //직렬적으로 실행
  printString('A', () => { //콜백 안에서 B실행
    printString('B', () => {//콜백 안에서 C실행
      printString('C', () => {        
      })
    })
  })  
}

printAll();

printString 함수의 두 번째 인자로 callback을 넣어 주었고, string을 출력한 후 callback함수가 실행되도록 했다. 이럴 경우 ABC가 순서대로 출력되지만, 시간차가 랜덤으로 발생하게 된다. 콜백함수는 어떤 작업을 마친 후 바로 다음 일을 진행하도록 하는데 효과적인 방법이다. 콜백 함수를 이용함에 있어서 error를 핸들링하는 방법은 아래와 같다.

somethingGonnaHappen((err, data) => {
  if (err) {
    console.log('ERROR!!');
    return;
  }
  return data;
})

콜백함수는 두개의 인자를 갖게 되는데, 앞은 에러가 발생했을 때 에러메시지 등을 띄울 수 있도록 작동하고, 뒤에 인자는 에러가 발생하지 않았을 경우 콜백의 결과 값으로 전달된다. (보통 if에 error를 쓰고 예외 사항에는 data가 될 수 있게 작성한다.

Callback은 아주 유용하게 사용되지만 콜백함수가 연속적으로 나열될 경우 가독성이 아주 많이 떨어진다는 문제점을 가지고 있다(callback hell). 이런 현상을 개선하기 위해 Promise라는 것이 있다.

Promise

프로미스 자체가 하나의 클래스이다. 함수를 선언할 때 콜백을 인자로 넣지 않고, 함수 안에 new Promise를 리턴해준다. 위에서 다뤘던 함수를 Promise를 이용해 나타내면 아래와 같다.

const printString = (string) => {
  return new Promise((resolve, reject) => {
    //reject에 대한 것은 나중에 다시(try, catch사용)
  setTimeout(
    () => {
      console.log(string)
      resolve()
    },
    Math.floor(Math.random() * 100) + 1
  )
  })
}

const printAll = () => {
  printString('A')
  .then(() => {
    return printString('B')
  })
  .then(() => {
    return printString('C')        
  })  
}
printAll();

Promise는 resolve와 reject를 인자로 갖는다. 콜백에서의 err와 data처럼, 이 두 인자는 콜백 실행이 문제가 없을 때 resolve를, 에러가 발생했을 때 rejcet가 실행되게 된다. 여기서 resolve가 사용되는 콜백들을 연결할 때 .then을 사용하게 된다. reject를 이용해 에러를 핸들링할 때는 .catch를 사용한다. then으로 계속 연결한 뒤 마지막에 catch를 사용해서 예외사항을 잡아낸다.

하지만 아래와 같은 코드의 경우 Promise에도 가독성이 매우 떨어지는 Hell이 발생할 수 있음을 볼 수 있다.

function goToCodestates() {
  return new Promise((resolve, reject) => {
    setTimeout(() => { resolve('1. Go to codestates') }, 100)
  })
}

function sitAndCode() {
  return new Promise((resolve, reject) => {
    setTimeout(() => { resolve('2. sit and code') }, 100)
  })
}

function eatLunch() {
  return new Promise((resolve, reject) => {
    setTimeout(() => { resolve('3. eat Lunch') }, 100)
  })
}

function goToBed() {
  return new Promise((resolve, reject) => {
    setTimeout(() => { resolve('4. go to Bed') }, 100)
  })
}

goToCodestates()
.then(data => {
  console.log(data)

  sitAndCode()
  .then(data => {
    console.log(data)

    eatLunch()
    .then(data => {
      console.log(data)

      goToBed()
      .then(data => {
        console.log(data)
      })
    })
  })
})

그래서 사용하게 되는 것이 Promise Chaning이다. 다음에 실행될 함수를 리턴해준다.

//(생략)
//Promise chaning
goToCodestates()
.then(data => {
  console.log(data)
  return sitAndCode()
})
.then(data => {
  console.log(data)
  return eatLunch()
})
  .then(data => {
  console.log(data)
  return goToBed()
})
  .then(data => {
    console.log(data)
})

async & await

최근 문법에는 async & await을 사용할 수 있다. 함수를 선언할 때 async를 넣어주고 .then을 사용하는 대신 await을 사용해서 실행함수들을 연결해 준다.

const result = async () => {
  const one = await goToCodestates();
  console.log(one)

  const two = await sitAndCode();
  console.log(two)

  const three = await eatLunch();
  console.log(three)

  const four = await goToBed();
  console.log(four)
}

result();
profile
새로운 도전을 멈추지 않는편👊 / 주어진 상황에 최선을 다하는 편🏋 / 아는 것을 전달하는 것에 보람을 느끼는 편💪

0개의 댓글