동기와 비동기

aydennote·2022년 5월 13일
0

JavaScript

목록 보기
2/7

📖 오늘 학습 뽀인트!

  1. 동기와 비동기
    1-1 promise
    1-2 async, await

1. 동기와 비동기

위 사진처럼 동기적인 작업은 1, 2, 3 작업순서대로 진행되는 방식이다. 비동기적인 작업은 1, 2, 3 순서와 상관 없이 1번이 처리되는 동안에도 2번이 실행될 수 있는 방식이다. 만약, 작업이 순차적으로 이루어져야 할 때 비동기 처리를 하면 그 순서가 보장되지 않는다.


❓ 비동기 처리가 필요한 이유가 뭘까?
서버로 데이터를 요청했을 때 서버가 언제 그 요청에 대한 응답을 줄지도 모르는데 마냥 다른 코드를 실행 안 하고 기다릴 순 없기 때문이다. 만약, 100개 요청을 보낸다고 가정하면 동기 처리는 코드 실행하고 기다리고, 실행하고 기다리고.. 를 반복할 것이다. 결국, 어플리케이션이 제대로 실행되지 않거나 많은 시간을 기다려야 될 것이다.

1-1 promise

// 콜백지옥
step1(function(value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        step5(value4, function(value5) {
            // value5를 사용하는 처리
        });
      });
    });
  });
});

자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백 함수를 사용한다. 위 소스코드는 콜백함수를 극단적으로 많이 사용한 코드로 흔히 콜백지옥이라고 말한다. 이 콜백지옥으로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며, 여러 개의 비동기 처리를 한번에 처리하는 데도 한계가 있다. 이러한 단점을 보완하여 만든 패턴이 promise이다.


❓ 그렇다면 promise를 언제 사용하게 될까?
서버에 ‘데이터 하나 보내주세요’ 라는 요청을 할 경우, 데이터를 받아오기도 전에 마치 데이터를 다 받아온 것 처럼 화면에 데이터를 표시하려고 하면 오류가 발생하거나 빈 화면이 뜬다. 이와 같은 문제를 막고자 비동기 동작을 동기적으로 받아 처리할 때 사용한다.


function sayHello(){
    return new Promise((resolve, reject)=>{
        const hello = "hello hello";
        resolve(hello)
        // reject(new Error()); 에러 발생시
    })
}
sayHello()
    .then(resolveData => {
        console.log(resolveData)
        return resolveData
    })
    .then(resolveData=>{
        console.log(resolveData)
        return resolveData
    })
    .then(resolveData=>{
        console.log(resolveData)
    })
    .catch(error=>{
    console.log(error)
})

프로미스를 사용하면 비동기 처리 후 마치 동기 메서드처럼 값을 반환할 수 있다. sayHello 라는 함수는 프로미스로 비동기적으로 작업이 실행된다. 비동기적으로 처리된 결과 값을 resolve 넘겨주고 sayHello 호출문에서는 then이라는 메서드로 동기적으로 반환 받을 수 있다.


✍ promise 처리 상태

const promise = new Promise((resolve, reject) => {
  // ...
});

pending (대기) 상태 : 비동기 처리 로직이 아직 완료되지 않은 상태
new Promise()생성자를 호출하면 대기 상태가 된다.


function sayHello() {
  return new Promise((resolve, reject) => {
    const data = 'hello'
    resolve(data);
  });
}

fullfilled (이행) 상태 : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태. 넘겨 받은 인자에서 resolve를 함수로 실행하면 이행 상태가 된다.


function sayHello() {
  return new Promise((resolve, reject) => {
    reject(new Error('fail!!!!'));
  });
}
sayHello()
  .then()
  .catch((error) => {
    console.log(error); // fail!!!!
  });

rejected (실패) 상태 : 비동기 처리가 실패하거나 오류가 발생한 상태. 넘겨 받은 인자에서 reject를 함수로 실행하면 실패 상태가 된다. 실패 상태가 되면 실패 처리의 결과값을 catch()메소드를 통해 받을 수 있다.

1-2 async, await

const promise = function () {
  return new Promise((resolve, reject) => {
    resolve("heeeeelo");
  });
};
async function sayHello() {
  const result = await promise(); // 프로미스 이행 상태가 될 때까지 기다렸다가,
  console.log(result); // 완료 되면 하단의 코드가 이어서 실행됨
}

sayHello();

async, await은 기존의 비동기 처리 방식인 콜백 함수와 promise 단점을 보완한 방식이다. promise의 비동기 처리 결과를 then, catch, finally로 후속 처리 없이 동기 처리처럼 사용할 수 있다.

***// 동기처리 //***
setTimeout(()=> {
    console.log('5초 끝!')
}, 5000);

setTimeout(()=> {
    console.log('10초 끝!')
}, 10000);

function cook(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const myCake = async () => {
    await cook(3000);
    return '케이크';
};

const myCoffee = async () => {
    await cook(2000);
    return '커피';
};

const myCookie = async () => {
    await cook(5000);
    return '쿠키';
};

async function asyncProcess() {
    const cake = await myCake();
    console.log(cake);
    const coffee = await myCoffee();
    console.log(coffee);
    const cookie = await myCookie();
    console.log(cookie);
}

asyncProcess(); 

***// 비동기처리 //***
setTimeout(()=> {
    console.log('5초 끝!')
}, 5000);

setTimeout(()=> {
    console.log('10초 끝!')
}, 10000);

function cook(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const myCake = async () => {
    await cook(3000);
    return '케이크';
};

const myCoffee = async () => {
    await cook(2000);
    return '커피';
};
const myCookie = async () => {
    await cook(5000);
    return '쿠키';
};

async function promiseProcess() {
   const results = await Promise.all([myCake(), myCoffee(), myCookie()]);
 console.log(results);
}

promiseProcess();

await을 많이 사용하면 동기적으로 값을 기다리기 때문에 함수가 그만큼 늦게 끝난다.
Promise.all([sayName(), sayHello(), sayBy()])를 사용해 all로 전달된 함수를 모두 비동기 실행할 수 있다. 이렇게 비동기로 실행된 결과를 await를 사용해 동기로 받고 console.log(results);를 했다. 만약, Promise.all 앞에 await이 없다면 result는 출력되지 않는다.

async, await 장점
1. promise 결과 값을 then, catch를 통해 다루는 것이 아닌,
변수의 담아 동기적 코드처럼 작성해줄 수 있다는 점에서 편리함을 제공한다.
2. promise catch가 아닌, 일반적인 코드와 동일한 try..catch 사용할 수 있다.
3. promise의 then 지옥을 피할 수 있다.


✍ async, await 주의
await 키워드는 async 함수 내부에서 사용해야 된다.
await 키워드는 promise 앞에서 사용해야 한다.
async 함수는 반환값을 resolve 하는 promise를 반환한다.

// 그냥 promise 사용
fetch('접속 URL')
    .then(function(response) {
        console.log(1);
        return response.json();
    })
    .then(json => {
        let 지역 = json.find(s => s['시·도별(1)'] == '전국')
        console.log(지역)
        console.log(지역['1차 접종 누계'])
        console.log(지역['2차 접종 퍼센트'])
    })

// async, await 사용
async function 접종퍼센트(지역){
  const response = await fetch(`접속 URL`);
  const data = await(response.json());
  const 접종퍼센트 = data.find(s => s['시·도별(1)'] == 지역)['2차 접종 퍼센트'];
  console.log(접종퍼센트)
}
접종퍼센트('전국')

위와 같이 동일한 결과를 promise, async, await 2가지 방법으로 받아 올 수 있다.

profile
기록하는 개발자 Ayden 입니다.

0개의 댓글