본문 바로가기

혼공스보면서 혼공스(JS)

CH5. 함수- 함수 고급

05-2 함수 고급

1. 동기 VS 비동기

  특징 장점 단점
동기 요청을 보낸 후 응답을 받아야만
다음으로 넘어간다.
설계가 간단하고 직관적이다 요청에 따른 결과가 반환되기 전까지
아무것도 못하고 대기해야한다.
-->비효율
비동기 요청을 보낸 후 응답과 상관없이
다음으로 넘어간다. 
요청에 따른 결과가 반환되는
시간 동안 다른 작업을 할 수 있다.
-->효율
설계가 복잡하다.
<script>
function asyncTimer(time){
    console.log('1')
    console.log('2')
    setTimeout(
        function(){
            console.log('3')
        }, time*1000
    )
    console.log('4')
    setTimeout(
        function(){
            console.log('5')
        },time*500
    )
}
asyncTimer(1)
</script>

<실행결과>
1
2
4
5
3

ㄴ 동기적으로 실행된다면 1과 2를 출력한 후 3이 출력될 때까지 1초를 기다릴 것이다.

     그러나 자바스크립트는 비동기적으로 실행하므로 1초를 기다리는 동안 4를 출력하고

     0.5초 후에 5를 출력하고 마지막으로 3이 출력된다.

 

2. 콜백 함수

1) 콜백 함수란?

setTimeout(
        function(){
            console.log('3')
        }, time*1000
    )

ㄴ 함수의 매개변수로 전달되는 함수를 콜백 함수라고 한다. 위에서는 function(){console.log('3')}가 콜백 함수이다.

 

2) 동기 콜백 함수와 비동기 콜백 함수

<script>
function printImmediately(print){
    print()
    console.log('world')
}
printImmediately(function () {console.log('hello')})
</script>

<실행결과>
hello
world

ㄴ console.log('hello') 함수는 printImmediately 함수에 매개변수로 전달되어 콜백 함수라고 할 수 있다.

     또한 함수 호출 후 시간 지체 없이 바로 실행되었으므로 동기 콜백 함수라고 할 수 있다.

 

<script>
function printWithDelay(print,timeout){
    setTimeout(print, timeout);
    console.log("world")
}
printWithDelay(function (){console.log("hello")},1000)
</script>

<실행결과>
world
hello

ㄴ console.log("hello") 함수는 printWithDelay 함수에 매개변수로 전달되어 콜백 함수라고 할 수 있다.

     함수 호출 후 console.log("world")를 먼저 실행하고 1초 뒤에 console.log("hello")가 실행이 되므로

     비동기 콜백 함수라고 할 수 있다.

 

3) 콜백 지옥 탈출- Promise

콜백 함수 안에서 또 다른 콜백 함수를 호출을 반복하면서 코드의 길이가 길어지고 가독성이 떨어지는 경우를 콜백 지옥이라고 한다. 이러한 콜백 지옥을 탈출할 수 있는 대표적인 방법 중 하나가 promise 객체를 사용하는 것이다.

const promise = new Promise(function(resolve,reject){
    //함수 실행
}) 

ㄴ Promise 객체가 전달하는 콜백 함수의 이름은 executor이고 promise가 생성되면 바로 실행된다.

     이때 네트워크 통신 등 불필요한 함수 실행을 피할 수 있도록 코드를 작성해야 한다.

 

<script>
var promise = function (flag) {
  return new Promise(function (resolve, reject) {
    if (flag) {
      resolve("resolved!");
    } else {
      reject("rejected!");
    }
  });
};

promise(true)
  .then(function (result) {
    console.log(result);
  })
  .catch(function (result) {
    console.log(result);
  });

promise(false)
  .then(function (result) {
    console.log(result);
  })
  .catch(function (result) {
    console.log(result);
  });
</script>

<실행결과>
resolved!
rejected!

ㄴ 성공적으로 실행하면 resolve 함수를 수행하고 .then 메소드로 연결되어 resolved! 를 결과로 출력한다.

     에러가 발생하면 reject 함수를 수행하여 .catch 메소드로 연결되고 rejected!를 결과로 출력한다.

 

4) 콜백 지옥 탈출- async/await

async function (){
    await asyncFunction1(){ ... }
    await asyncFunction2(){ ... }
}

ㄴ 비동기 코드일지라도 async와 await를 사용하면 동기적으로 수행이 가능하다.

 

<script>
async function print() {
  await timer(1, 3);
  await timer(2, 1);
  await timer(3, 2);}

function timer(num, time) {
  setTimeout(function () {
    console.log(num);
  }, time * 1000);}

print()
</script>

<실행결과>
2
3
1

ㄴ async/await를 사용했지만 예상과 달리 비동기적인 실행으로 결과가 나타났다. Promise를 사용하지 않았기 때문이다.

 

async function print() {
  await timer(1, 3)
    .then(function (result) {
      console.log(result);
    })
    .catch(function (result) {
      console.log(result);
    });

  await timer(2, 1)
    .then(function (result) {
      console.log(result);
    })
    .catch(function (result) {
      console.log(result);
    });

  await timer(3, 2)
    .then(function (result) {
      console.log(result);
    })
    .catch(function (result) {
      console.log(result);
    });
}

function timer(num, time) {
  return new Promise(function (resolve, reject) {
    setTimeout(console.log(num), time * 1000);
    resolve("resolved!");
    reject("rejected!");
  });
}

print();

<실행결과>
1
'resolved!'
2
'resolved!'
3
'resolved!'

ㄴ Promise 객체를 사용하니 동기적으로 실행되었고 결과로 1,2,3 이 도출되었다.

 

3. 화살표 함수

(매개변수) => {
    실행할 함수
}

ㄴ function 키워드 대신 화살표(=>) 를 사용하여 간단히 함수를 작성할 수 있다.

 

<script>
    let numbers=[1,2,3,4,5,6]
    
    numbers
        .filter((value) => value % 2 === 0)  --> 리턴하는 값이 true 인 것들만 모아서 새로운 배열 생성
        .map((value) => value * value)  --> 리턴한 값들을 기반으로 새로운 배열 생성
        .forEach((value) => {   --> 배열 내부의 요소를 사용하여 호출
            console.log(value)
        })
</script>

<실행결과>
4
16
36

ㄴ 위처럼 메소드가 리턴하는 값을 기반으로 함수를 줄줄이 사용하는 것을 메소드 체이닝이라고 한다.

4. 타이머 함수

함수 이름 설명
setTimeout(함수, 시간) 특정 시간 후에 함수를 한 번 호출한다
setInterval(함수, 시간) 특정 시간마다 함수를 호출한다
clearTimerout(타이머_ID) setTimeout() 함수로 설정한 타이머를 제거한다
clearInterval(타이머_ID) setInterval() 함수로 설정한 타이머를 제거한다
<script>
    setTimeout(()=>{
        console.log("1초 후에 실행됩니다")
    },1000)

    let count = 0
    let id

    id = setInterval(()=>{
        console.log(`1초마다 실행됩니다(${count}번째)`)
        count++
        if(count === 5)
            clearInterval(id)
    },1000)
</script>

<실행화면>
1초 후에 실행됩니다
1초마다 실행됩니다(0번째)
1초마다 실행됩니다(1번째)
1초마다 실행됩니다(2번째)
1초마다 실행됩니다(3번째)
1초마다 실행됩니다(4번째)