JS)동기,비동기(blocking,non-blocking)

백준우·2021년 10월 17일
0

JavaScript & TypeScript

목록 보기
12/15
post-thumbnail

1. 동기,비동기란

2. 비동기화를 사용하는 이유

3. 비동기적 코드 사용법

3.1 callback
3.2 Promise
3.3 async/await


1. 동기,비동기란

  • 위 사진과 같이 커피를 주문하는 상황에서 A가 커피주문을하고 커피가 나와 절달되어야지 다음사람이 커피를 주문할수 있는 상황(동기)와 커피를 주문하고 커피가 나올때마다 커피를 받으러 오면되는 상황(비동기)가 있다. 그림에서도 보시다시피 비동기상황이 좀더 효율적으로 보일것이다.
console.log('커피 주문을 받을수 있습니다.')
console.log('커피를 주문 받았습니다.')
console.log('커피가 나왔습니다.')
console.log('커피 주문을 받을수 있습니다.')
            ...
          


위 상황은 비동기적 상황이다. 코드를 위에서 부터 아래까지 쭉 일어가는 느낌이다.

console.log('커피 주문을 받을수 있습니다.)
console.log('커피를 주문 받았습니다.')
setTimeoug(()=> {console.log('커피가 나왔습니다.')},3000)
console.log('커피 주문을 받을수 있습니다.)


위 상황은 동기적 상황을 연출하였다. 위에서 부터 읽어내려오던 코드는 중간 setTimeout를 건너뛰고 밑의 함수부터 출력하다가 setTimeout함수는 함수가 완료되면 출력한다.

2. 비동기화를 사용하는 이유

우리가 사용하는 JavaScript는 single threaded언어이다. 따라서 한번에 하나의 작업밖에 수행하지 못한다.
따라서
1) 사용자 이벤트처리
2) 네트워크 응답처리
3) 파일을 일고 쓰는등의 파일 시스템 작업
4) 의도적으로 시간 지연을 사용하는 기능(알람 등)
를 구현하기 위해서는 JavaScript에서는 비동기화를 사용하여야 한다.
자세한 예시1)을 통해 JavaScript의 작동원리를 살펴보고 비동기화를 사용하는 이유를 실생활 예시로 살펴보겠다.

예시1)위 그림에서 첫번째로 stack에 100초가 걸리는 코드를 넣었을경우 사이트는 그상태에서 아무런 동작을 하지 못하게 된다.

예시2)그래프를 확인하더라도 많은 코드를 한번에 수행하는 비동기화는 매우 효율적이라는것을 알 수 있다.

예시3)흔히 우리가 접하는 Youtube의 화면 구성을 예시로 들었을때 화면출력부분이 로딩되고 있을때에도 비동기적인 활동으로 인해 다른 활동(재생목록 송출,출력화면 설명구성,검색바 버튼클릭 활성준비)을 할수 있다.

3. 비동기적 코드 사용법

3.1 callback

  • 콜백함수란 말 그대로 나중에 호출(call)되는 함수를 뜻한다.
  • 어떤 이벤트가 발생할시 특정 시점에 도달했을때 시스템에서 함수를 호출한다.

!)대표적인 콜백함수인 event litener

function callback() { alert("Hello");}
document.getElementById("meet").onclick = callback() //이벤트 발생시 callback함수 실행

!)함수의 실행 순서를 제어할 수 있다.

const print = (string,callback) =>{
  setTimeout(
    ()=>{
      console.log(string)
      callback()},
    2000)}
const printall = () => { 
  print('a',() =>{ // 실행이 되면 callback함수를 실행
    print('b',()=>{ // 실행이 되면 callback함수를 실행
      print('c',()=>{}) //실행이 되면 callback함수를 실행
    })
  })
}
printall() //2초간격으로 'a','b','c' 출력

!!)callback error handling Design

function checkfile(filePath, callback) { //callback이라는 에러를 처리하는 함수
   fs.readFile(filePath, "utf8", (error, data) => {//fs호출을 통해 에러유무 확인
        if (error) callback(error, null);//에러일경우 callback에 error 변수 대입
        else callback(null, data);//아닐시 정상적인 데이터를 콜백에 넣는다.
    })
}

주의!!


무분별한 콜백을통해 콜백지옥을 경험하면 어떤함수를 호출하는지 정확한 가독성이 매우 떨어진다.
이러한 가독성 문제를 해결하고자 나온것이 바로 Promise함수이다.

3.2 Promise

  • 프로미스는 주로 서버에서 데이터를 받아와 화면에 출력할때 사용합니다.
  • 데이터의 오류나 정상적 처리가 이루어졌을경우 함수를 출력합니다.

!) Promise3가지 형태

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
  • 대기(new Promise호출시 pending상태가 됩니다.)
new Promise(function(resolve, reject){ 
//정상적인 데이터 처리시 resolve, 데이터 처리 실패시 reject함수 인자를 호출한다.
});
  • 이행(resolve를 실행하면 이행상태가 됩니다)
function getData() {
  return new Promise(function(resolve, reject) {
    const data = 'Promise'
    resolve(data); //resolve함수에 data값을 넣는다.
  });
}
// then을 통해 Pushdata함수의 데이터를 받아온다
// resolve()의 결과 값 data를 resolvedData로 받음
getData()
  .then((readData)=> {
  console.log(readData); //'Promise'
});
  • 실패(reject를 호출하면 실패상태가 됩니다)
function getData() {
  return new Promise(function(resolve, reject) {
    const data = 'Promise'
    resolve(data)
    const none = 'none Data'
    reject(new Error(('data'))); //resolve함수에 data값을 넣는다.
  });
}
// then을 통해 Pushdata함수의 데이터를 받아오며 catch로
// resolve()의 결과 값 data를 resolvedData로 받음
getData()
.then((readData)=> {
  console.log(readData); //'Promise'
})
.catch((err)=> {
  console.log(err); // none Data
});
  • 여러개의 Promise를 연결(Promise Chaining)
new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})
.then(function(result) { //Promise로 부터 값을 받아온다.
  console.log(result); // 1
  return result + 1; //return으로 다음 then에 값을 전달한다.
})
.then(function(result) { //이전 then으로 부터 값을 받아온다.
  console.log(result); // 2
  return result + 1; //return으로 다음 Promise에 값을 전달한다.
})
.then(function(result) {
  console.log(result); // 3
});

중요!! then은 Promise를 반환한다.

Promise 객체를 반환한다는 것은 then, catch메소드를 사용할 수 있다는 것을 뜻하며, 이를 통해 연속적으로 then메소드를 사용하여 Promise chaining이 가능하다는 것을 의미한다.

Tip) Promise.all

  • Promise.all은 요소 전체가 Promise인 배열을 받고 Promise가 모두 처리되면 새로운 프라미스를 반환한다.(배열 요소 순서는 Promise.all에 전달되는 프라미스 순서와 상응한다.)
  • Promise.all에 전달되는 프라미스 중 하나라도 거부되면, Promise.all은 즉시 거부되고 배열에 저장된 다른 프라미스의 결과는 완전히 잊혀지며 Promise.all 전체의 결과는 에러가 된다.
  • Promise.all의 첫 번째 프라미스는 가장 늦게 이행되더라도 처리 결과는 배열의 첫 번째 요소에 저장된다.
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];

// fetch를 사용해 url을 프라미스로 매핑합니다.
let requests = urls.map(url => fetch(url));

// Promise.all은 모든 작업이 이행될 때까지 기다립니다.
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));

[출처: https://lienkooky.tistory.com/98 [Lien _ Blog]]

3.3 async/await

-기존의 콜백 함수와 프로미스의 단점을 보완하고 가독성 좋은 코드를 작성할 수 있게 해준다.

Promise VS async/await

  1. Promise
function get(){
  return new Promise((resolve,reject) => {
    resolve('done')})}
  1. async/await
async function get() {
  return done}

둘다 같은 Promise를 반환한다.

  • 기본틀
async function 함수명() {
  await 비동기_처리_메서드_명();
}
  • 예제1)
function getData() {
  return new Promise(function(resolve, reject) {
    const data = 'Promise'
    resolve(data); //resolve함수에 data값을 넣는다.
  });
}
// then을 통해 Pushdata함수의 데이터를 받아온다
// resolve()의 결과 값 data를 resolvedData로 받음
async function readData(){
  const resultData = await getData()
  console.log(resultData) //'Promise'
//await를 사용하지 않았다면 콜백이나 .then을 사용했어야 합니다. 하지만 async await 문법덕분에 코드가 간결해지며 비동기에 대한 사고를 적게하면 됩니다.
  • 예외처리)
function getData() {
  return new Promise(function(resolve, reject) {
    const data = 'Promise'
    resolve(data); //resolve함수에 data값을 넣는다.
    const none = 'none Data'
    reject(new Error(('data'))); //resolve함수에 data값을 넣는다.
  });
}
// then을 통해 Pushdata함수의 데이터를 받아온다
// resolve()의 결과 값 data를 resolvedData로 받음
async function readData(){
  const resultData = await getData()
  console.log(resultData) //'Promise'
  try{
    await getData()
  }catch(err){
    console.log(err)
  }
}  
OR
async function readData(){
  const resultData = await getData()
  console.log(resultData) //'Promise'
  try{
    await getData().catch((err) => console.log(err))
  }catch(err){
    console.log(err)
  }
}  
OR
async function getData(){
  throw 'none Data'}
getData().catch(){(err)=>{
  console.log(err) //'none Data'
}
//await를 사용하지 않았다면 콜백이나 .then을 사용했어야 합니다. 하지만 async await 문법덕분에 코드가 간결해지며 비동기에 대한 사고를 적게하면 됩니다.
profile
이게 되네?

0개의 댓글