[JS] 비동기 (1)

김다빈·2023년 8월 24일
0

자바스크립트

목록 보기
16/36
post-thumbnail

복습할 내용

  • promise 복습 필요!!!

📌 동기(Synchronous)와 비동기(Asynchronous)

동기 : 순차적으로 코드 실행 ⭕
비동기 : 순차적으로 코드 실행 ❌

예시

console.log(1)
setTimeout(() => {
  console.log(2)
}, 1000);
console.log(3)

결과

1
3
2 //위의 결과들이 출력되고 1초 후에 출력됨

여기서 알 수 있는 점 ➡️ setTimeout 함수를 사용하면 비동기 처리된다. (순차적으로 코드가 실행되지 않는다는 뜻)


fetch 함수

특정한 웹주소로 데이터를 보낼때 사용(= 요청, request),
서버에서 답이 오면(= 응답, response) 해당 객체를 반환

서버에 요청을 하고, 응답을 받는데는 얼마나 걸릴지는 모르지만 어쨌든 시간이 걸림

자바스크립트는 시간이 걸리는 코드를 동작할 때는 서버에서 답이 올때까지 기다리면서 다른 동기 코드를 실행하고 동기 코드가 모두 실행된 후에 비동기 코드를 실행한다.

예시

fetch('https://www.omdbapi.com/?apikey=d64da9e1&s=frozen')
.then(res => res.json())
.then(res => console.log(res))

console.log(1)
console.log(2)
console.log(3)

결과

더 뒤에 작성한 내용이 먼저 출력되고 앞에 작성한 fetch~ 결과가 더 나중에 출력됨

만약 순차적으로 실행하고 싶다면 (=fetch 내용이 모두 실행된 뒤에 콘솔 1,2,3 을 찍고싶다면 해당 내용을 then 내부로 넣어줘야 함

fetch('https://www.omdbapi.com/?apikey=d64da9e1&s=frozen')
.then(res => res.json())
.then(res => 
      console.log(res)
      console.log(1)
      console.log(2)
      console.log(3)
)

📌 콜백 지옥

동기 패턴

const a = () => console.log(1)
const b = () => console.log(2)

a()
b()

실행 결과

1
2

비동기 패턴

비동기 패턴에서 실행 순서를 보장하기 위해 콜백 패턴을 사용할 수 있다.

const a = (callback) => {
  setTimeout(() => {
    console.log(1)
    callback()
  }, 1000);
}
const b = () => console.log(2)

a(() => {
  b()
})

실행 결과

//저장하고 1초 뒤 아래 결과 동시에 출력됨
1
2

const a = (callback) => {
  setTimeout(() => {
    console.log(1)
    callback()
  }, 1000);
}
const b = () => {
  setTimeout(() => {
    console.log(2)
  }, 1000);
}

a(() => {
  b()
})

실행 결과

//저장하고 1초 뒤 1 출력
1
//1이 출력되고 1초 뒤 2 출력
2

✅ 콜백 지옥

const a = (callback) => {
  setTimeout(() => {
    console.log(1)
    callback()
  }, 1000);
}
const b = (callback) => {
  setTimeout(() => {
    console.log(2)
    callback()
  }, 1000);
}
const c = (callback) => {
  setTimeout(() => {
    console.log(3)
    callback()
  }, 1000);
}
const d = (callback) => {
  setTimeout(() => {
    console.log(4)
  }, 1000);
}

a(() => {
  b(() => {
    c(() => {
      d()
    })
  })
})

실행 결과

//저장하고 1초 뒤 1 출력
1
//1이 출력되고 1초 뒤 2 출력
2
//2가 출력되고 1초 뒤 3 출력
3
//3이 출력되고 1초 뒤 4 출력
4

그렇게 계속 콜백 패턴을 사용하다보면 함수 호출 시 계속 들여쓰는 형태로 작성하게되고, 이런 현상을 콜백 지옥 이라 부른다.

콜백 지옥은 작성뿐만 아니라 해석도 어렵다.

영화 예제

const getMovies = (movieName, callback) => {
  fetch(`https://www.omdbapi.com/?apikey=d64da9e1&s=${movieName}`)
    .then(res => res.json())
    .then(res => {
      console.log(res);
      callback();
    })
}

getMovies('frozen', () => {
  console.log('겨울왕국!')
})

getMovies('avengers', () => {
  console.log('어벤져스!')
})

getMovies('propose', () => {
  console.log('프러포즈!')
})

실행 결과

실행할 때마다 결과의 순서가 바뀜
➡️ 서버에서 결과를 가져오는데 걸리는 시간에 따라 출력 순서가 달라짐

순서를 보장해서 출력하기 위해선 콜백 안에 넣고, 콜백 안에 넣고... 반복하는 콜백 지옥 이 시작된다.

getMovies('frozen', () => {
  console.log('겨울왕국!')
  getMovies('avengers', () => {
    console.log('어벤져스!')
    getMovies('propose', () => {
      console.log('프러포즈!')
    })
  })
})

이렇게 불편한 콜백 지옥을 대체하기 위해 promise 라는 클래스를 사용할 수 있다. promise 클래스를 사용하면 보다 관리를 용이하고 읽기 쉬운 코드를 작성할 수 있다.

📌 Promise

콜백이라는 매개변수를 대신해서 resolve 라는 개념으로 실행 위치를 보장할 수 있다.

new 키워드를 사용해서 promise 객체를 만들어 반환한다.

const a = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(1);
      resolve();
    }, 1000);
  })
}
const b = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(2);
      resolve();
    }, 1000);
  })
}
const c = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(3);
      resolve();
    }, 1000);
  })
}
const d = () => console.log(4);

a().then(() => {
  b().then(() => {
    c().then(() => {
      d()
    })
  })
})

실행 결과

//저장하고 1초 뒤 1 출력
1
//1이 출력되고 1초 뒤 2 출력
2
//2가 출력되고 1초 뒤 3, 4 출력
3
4

promise 클래스를 사용했음에도 불구하고 여전히 호출 시에 콜백 지옥과 같이 파고드는 형태가 발생. 콜백 지옥과 다를게 없음

//더 간단하게 호출 가능
a().then(() => {
  return b()
}).then(() => {
  return c()
}).then(() => {
  d()
})

//중괄호 다음에 바로 return 키워드 나오면 생략 가능
a()
  .then(() => b())
  .then(() => c())
  .then(() => {
    d()
  })

//then() 메소드에서 함수를 전달할 때 그냥 함수 자체를 전달할 수 있음
a()
  .then(b)
  .then(c)
  .then(d) //.then(() => console.log(4)) 과 같음

a함수를 실행하면 then(에 있는 b함수가 promise의 resolve로 들어가서) b함수가 호출되고 다시 c가 b의 resolve로 들어가서 호출되고 실행되고... 반복

같은 방식으로 동작하지만 코드가 훨씬 간결해짐

영화 예제

const getMovies = (movieName) => {
  return new Promise(resolve => {
    fetch(`https://www.omdbapi.com/?apikey=d64da9e1&s=${movieName}`)
      .then(res => res.json())
      .then(res => {
        console.log(res);
        resolve();
      })
  })
}

getMovies('frozen')
  .then(() => {
    console.log('겨울왕국!')
    return getMovies('avengers')
  }).then(() => {
    console.log('어벤져스!')
    return getMovies('propose')
  }).then(() => {
    console.log('프로포즈!')
  })

then 메소드 사용할 때 전달할 콜백함수를 꼭 return 키워드를 사용해서 전달할 수 있도록 한다.


📌 async, await 패턴

await : promise 객체 를 반환하는 함수의 앞에 붙여 사용할 수 있다.

const a = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(1);
      resolve();
    }, 1000);
  })
}
const b = () => console.log(2)

const wrap = async () => {
  await a()
  b()
}

wrap()
// 1
// 2

await 키워드를 사용해서 어떤 함수(코드)를 실행하려면 해당 내용을 호출하는 함수 앞에 꼭 async 키워드를 붙여줘야 한다.

즉, async 가 붙은 함수가 있어야만 그 안에서 await 를 사용할 수 있다.

const a = async () => {
  await b()
}

대부분 await 가 붙는 코드의 위치는 함수 내부일 것이므로 그냥 함수 앞에 async 만 붙여서 비동기처리를 한다고 생각하면 된다.

영화 예제

const getMovies = movieName => {
  return new Promise(resolve => {
    fetch(`https://www.omdbapi.com/?apikey=d64da9e1&s=${movieName}`)
      .then(res => res.json())
      .then(res => {
        console.log(res);
        resolve();
      })
  })
}

const wrap = async () => {
  await getMovies('frozen')
  console.log('겨울왕국!')
  await getMovies('avengers')
  console.log('어벤져스!')
  await getMovies('propose')
  console.log('프로포즈!')
}

wrap()

실행 결과

profile
Hello, World

0개의 댓글

관련 채용 정보