[JS] 비동기처리 문제 해결을 위한 콜백함수, promise, async/await

JUNGHUN KIM·2022년 1월 3일
0

콜백함수란?

1. 다른 함수에 매개변수로 넘겨준 함수
2. 어떤 이벤트가 발생한 후, 수행될 함수 
3. 어떤 함수를 실행시키고 난 이후에 결과를 받을 함수 혹은 그 다음에 실행될 함수
  • JS에서 함수는 일급 객체이므로 매개변수로 함수를 전달이 가능하기 때문에 콜백함수가 가능.(일급 객체에 대해서는 아래 내용 참조)
  • 콜백함수는 클로저이기 때문에, 함수가 선언되었을때의 환경을 기억함.
  • 콜백함수는 비동기 처리의 문제점을 해결해줌(콜백함수는 비동기만을 처리하는 것은 아님. 이벤트처리에도 이벤트 리스너에는 콜백함수를 사용.)

비동기 처리

특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 것.
function findUser(id) {
  let user;
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
  }, 100);
  return user;
}

const user = findUser(1);
console.log("user:", user); // user: undefined waited 0.1 sec.

자바 스크립트 비동기 처리 특성상 특정 로직의 실행이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행해버리기 user의 결과값이 undefined이 나오게 됨(setTimeout이 끝나기 전에 return을 먼저 시켜버림)

setTimeout과 같은 비동기 함수를 호출하게 될 경우, 함수의 실행이 완료되기 전에 다음라인이 실행되버리기 때문에 주의가 필요하며,콜백함수, promise, async/await를 사용하여 비동기 처리를 해주어야 원하는 결과값을 얻을 수 있다.

콜백함수로 비동기 처리 문제점 해결

콜백함수로 비동기 처리

function findUserAndCallBack(id, cb) {
  setTimeout(function () {
    console.log("waited 0.1 sec.");
    const user = {
      id: id,
      name: "User" + id,
      email: id + "@test.com",
    };
    cb(user);
  }, 100);
}

findUserAndCallBack(1, function (user) {
  console.log("user:", user);
});

//waited 0.1 sec.
//user: {id: 1, name: "User1", email: "1@test.com"}

findUserAndCallBack를 실행시키며 첫번째 인자로는 유저 id를
함수의 두번째 인자로 콜백함수를 넘겼고
findUserAndCallBack함수 실행후 setTimeout으로 인해 0.1초 후
변수 user에 id, name, email이 할당되고 그 후 콜백함수가 실행되며
원하는 값이 console.log()로 찍히게 된다.

콜백지옥

비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제

이벤트 처리, 서버통신과 같은 비동기적인 작업을 수행하기 위해 콜백함수를 여러번 사용하다보니, 가독성이 떨어지고(들여쓰기가 깊어짐) 코드가 수정하기 어려운 현상을 콜백지옥이라고 함.

setTimeout(function (name) {
  let coffeeList = name;
  console.log(coffeeList);
  
  setTimeout(function (name) {
    coffeeList = `${coffeeList}, ${name}`
    console.log(coffeeList);
    
    setTimeout(function (name) {
      coffeeList = `${coffeeList}, ${name}`
      console.log(coffeeList);
      
      setTimeout(function (name) {
        coffeeList = `${coffeeList}, ${name}`
        console.log(coffeeList);
      }, 500, 'Affogato');
    }, 500, 'Caffe Latte');
  }, 500, 'Americano');
}, 500, 'Espresso');
// Espresso
// Espresso, Americano
// Espresso, Americano, Caffe Latte
// Espresso, Americano, Caffe Latte, Affogato

Promise

자바스크립트 비동기 처리에 사용되는 객체

기존에는 비동기 작업을 처리하기 위해서는 콜백함수로 처리해야 했지만, 콜백함수를 중첩해서 사용하게 되다보면, 코드의 가독성이 안좋아지게되고 유지보수가 어려워 지게 되는데 이러한 문제점을 해결하기 위해 ES6에서 새로 도입된 기능.

코드의 깊이가 깊어지는것을 방지하고 유지보수를 하기 쉽게 만들어줌.

Promise가 필요한 이유

프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 떄 사용.
자바스크립의 경우 비동기 처리방식으로 동작하는데, 비동기 처리를 하게 될경우 서버에서 데이터를 받아오기전에 코드 실행이 종료가 되버릴수 있는 문제점이 있으므로, 이러한 문제점을 해결하기 위한 한가지 방법.

Promise 3가지 상태

. new Promise()로 프로미스를 생성하고 종료될 때까지 3가지 상태를 가짐

  • Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
  • Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
  • Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태

new Promise() 메서드를 호출하게 되면 대기상태가 되며,
해당 메서드를 호출할때, 콜백 함수를 선언할수 있고 이때 콜백함수의 인자는 resolve와, reject를 줄 수 있다.

resolve같은 경우는 서버에서 데이터를 잘 받아 왔을때, then()을 이용하여 처리 결과값을 받을 수 있으며,
reject같은 경우 서버에서 데이터를 잘 받아오지 못하였거나, 에러가 발생하였을 경우 catch()를 사용하여 실패한 이유에 대해서 확인이 가능하다.

promise로 비동기 처리

function findUser (id) {
	return new Promise(function(resolve){
    	setTimeout(function(){
        	console.log("waited 0.1 sec.")
          	const user = {
            	id : id,
            	name : "User" + id,
            	email : id + "@test.com",
            };
          	resolve(user)
        },100)
    })
}


findUser(1).then(function(user){
	console.log("user :" , user )
})
//waited 0.1 sec.
//user: {id: 1, name: "User1", email: "1@test.com"}

콜백함수를 인자로 넘기지 않고, Promise 객체를 생성하여 Promise객체를 리턴하였고,
리턴받은 Promise객체에 then()메서드를 호출하여 해당 결과값을 받은 후 실행할 로직을 수행

Promise로 콜백 지옥 탈출(Promise Chaining)

Promise의 특징은 여러개의 프로미스를 연결하여 사용이 가능하다.
then()메서드를 호출하고 나면 새로운 프로미스 객체가 반환 되며 이를 이용하여 아래와 같은 로직으로 구현이 가능.

const addCoffee = function (name){
  return function (prevName){
    return new Promise(function (resolve){
      setTimeout(function () {
        const newName = prevName ? `${prevName} , ${name}` : name;
        console.log(newName);
        resolve(newName);
      }, 500);
    });
  };
};

addCoffee('Espresso')()
    .then(addCoffee('Americano'))
    .then(addCoffee('Caffe Latte'))
    .then(addCoffee('Affogato'));
    
// Espresso
// Espresso, Americano
// Espresso, Americano, Caffe Latte
// Espresso, Americano, Caffe Latte, Affogato

Async, await

자바스크립트 비동기 처리 패턴중 가장 최근에 나온 문법이며, 비동기 처리 방식인 콜백함수와 프로미스의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성 할 수 있게 도와줌.

asyncs / await을 사용한 비동기 처리

//promise객체를 반환하는 함수 addCoffee (promise는 비동기 처리를 위한 객체)
const addCoffee = function (name){
  return new Promise(function (resolve){
    setTimeout(function () {
      resolve(name);
    }, 500);
  });
};

const coffeeMaker = async function(){
  let coffeeList = '';
  const _addCoffee = async function(name){
    coffeeList += (coffeeList ? ',' : '') + await addCoffee(name);
  };
  
  await _addCoffee('Espresso');
  console.log(coffeeList);
  
  await _addCoffee('Americano');
  console.log(coffeeList);
  
  await _addCoffee('Caffe Latte');
  console.log(coffeeList);
  
  await _addCoffee('Affogato');
  console.log(coffeeList);
};

coffeeMaker();

// Espresso
// Espresso, Americano
// Espresso, Americano, Caffe Latte
// Espresso, Americano, Caffe Latte, Affogato

함수 앞에 async(예약어)를 표기하고
함수 내부에서 비동기 작업이 필요한 위치앞에 await을 표기하면
await 뒤의 내용은 promise로 반환되고 resolve된 이후 다음으로 진행 됨.

예외처리

async / await에서 예외처리를 하기 위해서는 try catch문을 사용해야함.
프로미스에서 에러처리를 위해 .catch()를 사용한것 처럼 async에서는 catch{}를 사용하면 됨.

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

일급 객체(First Class Object

다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체

일급 객체 조건

1. 변수나 데이터구조안에 담을 수 있다.

let foo = function(){
	console.log('일급 객체')
}

foo() // '일급 객체'

2. 파라미터(매개변수)로 전달 할 수 있다.

let foo = function() {
	let x = 10;
  	return x;
}

let goo = function(value){
	console.log(value)
}

goo(foo()) // 10

3. return값으로 사용할 수 있다.

let foo = function(){
	return function(){
    	let x = 10;
      	return x;
    }
}

console.log(foo()) // function(){ let x = 10; return x}
console.log(foo()()) // 10 

참조
https://victorydntmd.tistory.com/46
https://victorydntmd.tistory.com/48
https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/
https://joshua1988.github.io/web-development/javascript/js-async-await/
https://www.daleseo.com/js-async-callback/
https://taenami.tistory.com/m/111

profile
개발자가 되고 싶은 일문학도

0개의 댓글

관련 채용 정보