Frontend Programming/Javascript

자바스크립트 비동기 처리

yoonstar* 2021. 2. 18. 15:18

자바스크립트

single thread 언어, 즉 이벤트를 처리하는 콜 스택이 하나뿐임

 

그렇기 때문에 여러개의 이벤트를 처리할 때 동기적으로 처리하게 되면 모두 처리될 때까지 다른 업무를 수행하지 못하는 문제가 발생

 

예를 들어 화면에서 서버로 데이터를 요청했을 때 서버가 언제 그 요청에 대해 응답해줄지도 모르는데 마냥 다른 코드들을 실행안하고 기다릴 수 없음

 

따라서 즉시 처리하지 못하는 이벤트들은 web API로 보내서 콜스택이 비어있을 때에 먼저 처리된 이벤트들을 줄세워 다시 이벤트 큐에 줄 세워놓음 (들어온 순서는 중요하지 않고 어떤 이벤트가 먼저 처리되느냐가 중요)

 

정리하면, 자바스크립트는 동기적이지만 비동기식으로 동작하도록 조작할 수 있음

 

비동기적 방법에는 대표적으로 callback 함수 / promises / async & await 있음

 

동기(Synchronous) : 요청을 보낸 후 해당 요청의 응답을 받아야 다음 동작을 실행하는 방식

비동기(Asynchronous) : 요청을 보낸 후 응답과 관계없이 다음 동작을 실행할 수 있는 방식


비동기식으로 처리하는 방법

1. Callback 함수

어떤 일을 다른 객체에게 시키고, 그 일이 끝나는 것을 기다리는게 아니라 그 객체가 나를 다시 부를 때까지 내 할일을 하는 것 (non-block)

 

대표적으로 setTimeout 함수

console.log('1');
setTimeout(() => console.log('2'), 1000);
console.log('3');
//결과 : 1 3 2

 

단점 

가독성이 떨어짐

모든 콜백에서 각각 에러 핸들링 해줘야함

일명 콜백 지옥

 

2. Promise 객체

비동기 작업이 (성공 or 실패로) 완료된 후의 결과 값을 받을 수 있음

결과 값을 돌려받을 수 있기 때문에 이후 처리를 컨트롤 할 수 있게됨

 

상태

pending(대기)

fulfilled(이행)

rejected(실패) 

 

생성하기

Promise는 함수를 인자로 받고, 인자로 들어온 함수는 다시 resolve와 reject 2개의 함수를 인자로 받음

resolve는 비동기 처리 성공 시, reject는 비동기 처리 실패 시 호출됨 (reject의 인자로는 오류메시지)

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
    //reject("오류메시지");
  }, 1000);
});

 

사용하기

resolve시 then (promise에서 값이 정상적으로 수행된다면)

promise
  .then(val=>console.log(val));
 
//val은 위의 resolve에서 전달한 'success'라는 값

then은 다시 promise를 반환하기 때문에 promise chaining으로 연속 호출 가능

 

reject시 catch

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("error");
  }, 1000);
});

promise                               
  .then(val=>console.log(val))        //Promise Chaining
  .catch(err=>console.log(err));
  .finally(()=>console.log('finally');  //finally는 성공, 실패 상관없이 무조건 실행

 

사용 예시 코드

posts에 새로운 post를 추가하는 코드이다

const posts = [
  {
    title: "POST one",
    body: "BODY one",
  },
  {
    title: "POST two",
    body: "BODY post two",
  },
];

function addPost(post) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      posts.push(post);
      const error = false;
      if (!error) {
        resolve();
      } else {
        reject("error: something is wrong");
      }
    }, 2000);
  });
}

addPost({ title: "POST three", body: "BODY three" })
  .then(getPosts) // ()=>getPosts()
  .catch((err) => console.log(err));

 

Promise Chaining 

앞서 말했듯이 then 메서드는 promise를 다시 반환하기 때문에 연결해서 호출할 수 있다

const fetchNumber = new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 1000);
});

fetchNumber //반환값이 이후 then 함수의 인자로 전달됨
  .then((num) => num * 2) //여기서의 반환값이 이후 then 함수의 인자로 전달됨
  .then((num) => num * 3) 
  .then((num) => {
    return new Promise((resolve, reject) => { //새로운 promise를 전달할 수도 있음
      setTimeout(() => resolve(num - 1), 1000);
    });
  })
  .then((num) => console.log(num));  //.then(console.log) 

  

이 예제에서와 같이 then메서드를 통해서 값을 바로 전달할수도 있지만, 새로운 promise를 생성하여 전달할 수도 있다

 

TIP

.then(val=>getValue(val))

.then(getValue) //이렇게 생략 가능

 

Error Handling (오류 처리)

const getHen = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve("닭"), 1000);
  });
const getEgg = (hen) =>
  new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("Error: egg 에러발생")), 1000);
  });
const cook = (egg) =>
  new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => 요리`), 1000);
  });

getHen()
	.then(getEgg)
	.then(cook)
	.then(console.log)
	.catch(console.log);
//getEgg에서 에러가 발생했지만 에러가 마지막 줄로 전달되면서 catch가 잡힘!

//Error: Error: egg 에러발생

 

getHen()
  .then(getEgg)
  .catch((err) => { //getEgg의 에러 바로 잡아버리기
    return "빵";
  })
  .then(cook)
  .then(console.log)
  .catch(console.log);
//결과 : 빵 => 요리

이렇게 바로 다음 문장에서 에러처리를 해서 전체 promise chain이 실패하지 않도록,

다른 값을 대안으로 넘겨주는 방법! (다음 then으로 이동하지 않고 catch에서 받게 됨)

 

Promise.all

여러개의 비동기 작업들이 있고 이들 모두 완료되었을 때 처리하고 싶다면 Promise.all 이용

 

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("first success");
    resolve("1");
  }, 1000);
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log("second success");
    resolve("2");
  }, 2000);
});

Promise.all([promise1, promise2]).then((val) => {
  console.log("all success", val);
});

^ 결과

 

3. Async & Await

기존의 promise 위에 조금 더 간편한 API를 제공

 

주의할 점은 async와 await은 promise를 대체하는 것이 아니라는 것 !

promise를 사용하지만 then과 catch 메소드를 사용하는 것이 아닌 동기적 코드처럼 반환 값을 변수에 할당하여 작성할 수 있게끔 도와주는 문법

 

async : 함수 안에 async 키워드를 작성함으로써 자동으로 해당 블록이 promise가 되고 promise를 return함

async function fetchUser (){
  return 'anna';
}
const user=fetchUser();
user.then(console.log); //결과: anna

 

await : async 키워드가 붙은 함수 안에서만 사용가능 

 

async & await 기본 구조

async function 함수명(){
  await 비동기_처리_메서드_명();
}

await 키워드가 붙은 비동기처리 메서드는 반드시 프로미스 객체를 반환해야함!!

 

사용 예제

function fetchItems() {
  return new Promise(function(resolve, reject) {
    setTimeout(() => {
      var items = [1,2,3];
    resolve(items)
    }, 2000);
  });
}

async function logItems() {
  var resultItems = await fetchItems();
  console.log(resultItems); // [1,2,3]
}

 

const getData=async()=>{
	const response=await fetch(url); //await은 응답이 올때까지 기다려서 변수에 넣음
    const data=await response.json();
    console.log(data);
}
getData();

 

async와 await으로 비동기 통신 예제

function fetchUser() {
  const url = "https://jsonplaceholder.typicode.com/users/1";
  return fetch(url).then((res) => res.json());
}

function fetchTodo() {
  const url = "https://jsonplaceholder.typicode.com/todos/1";
  return fetch(url).then((res) => res.json());
}

async function logTodoTitle() {
  const user = await fetchUser();
  if (user.id === 1) {
    const todo = await fetchTodo();
    console.log(todo.title); 
  }
}
logTodoTitle(); // delectus aut autem

 

async와 await에서 예외처리는?

async function logTodoTitle(){
  try{
    const user=await fetchUser();
    if(user.id===1){
      const todo=await fetchTodo();
      console.log(todo.title);
    }
  }
  catch(error){
    console.log(error);
  }
}

 

 

[References] 

드림코딩 앨리 강의

www.youtube.com/watch?v=s1vpVCrT8f4

JS 비동기처리

velog.io/@yejinh/%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0

콜스택 관련

new93helloworld.tistory.com/358

async & await

joshua1988.github.io/web-development/javascript/js-async-await/

'Frontend Programming > Javascript' 카테고리의 다른 글

JSON 과 LocalStorage  (0) 2021.04.24
안티패턴  (0) 2021.03.02
sort 함수  (0) 2021.02.12
JSON  (0) 2021.02.12
random으로 값 가져오기  (0) 2021.02.11