js 비동기 처리 (callback, promise, async/await)

https://joshua1988.github.io/web-development/javascript/javascript-asynchronous-operation/ 에서 참조하여 작성했습니다.

비동기 처리 -> JS의 특징으로 instruction이 끝날 때까지 process를 멈추지 않고 다음 instruction을 실행하는 특성

이런 비동기처리 방식의 특징을 갖는 js에서 동기처리를 해야할 때가 있다면?

예시)

// #1
console.log('Hello');
// #2
setTimeout(function () {
 console.log('Bye');
}, 3000);
// #3
console.log('Hello Again');


위와 같은 코드에서 동기식 처리에서는 hello가 나오고 3초후 Bye 그리고 Hello Again이 나와야한다.

하지만 비동기식 처리에서는 hello가 나오고 hello again이 나오고 3초후 Bye가 나온다.


1. 콜백 함수형


function getData() {
 var tableData;
 $.get('https://domain.com/products/1', function (response) {
  tableData = response;
 });
 return tableData;
}

console.log(getData()); // undefined

예를 들어 위와 같은 코드에서 getData() 함수를 살펴보자.

getData에는 $.get으로 1번째 상품의 정보를 얻어온후 이 값을 콜백함수의 인자인 response로 넘겨 tableData로 주는 것을 확인할 수 있다.

만약 callback 함수로 처리하지 않았을 경우 HTTP GET request로 부터 받은 response가 오기도 전에 tableData를 리턴할 수 있다.

이는 callback 함수에 해당하는 함수를 따로 만들어 다음과 같이 작성할 수 있다.


function getData(callbackFunc) {
 $.get('https://domain.com/products/1', function (response) {
  callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
 });
}

getData(function (tableData) {
 console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});


문제는 다음과 같이 콜백함수를 연속으로 사용할 때이다.

$.get('url', function (response) {
 parseValue(response, function (id) {
  auth(id, function (result) {
   display(result, function (text) {
    console.log(text);
   });
  });
 });
});
이럴 경우 다음과 같이 수정가능하다.

function parseValueDone(id){
 auth(id, authDone);
}
function authDone(result) {
 display(result, displayDone);
}
function displayDone(text) {
 console.log(text);
}
$.get('url', function (response) {
 parseValue(response, parseValueDone);
});



2. promise 형

비동기처리 방식을 좀 더 간단하게 사용하는 방법이 있다.

function getData() {
  return new Promise(function (resolve, reject) {
    $.get('url 주소/products/1', function (response) {
      resolve(response);
    });
  });
}

getData().then(function (tableData) {
  console.log(tableData);
});


바로 Promise이다. Promise를 사용하려면 총 3가지의 상태를 기억하면 된다.

1. pending 상태: 비동기 처리 로직이 아직 완료되지 않은 상태
2. fulfilled 상태: 비동기 처리가 완료하여 결과 값을 반환해준 상태
3. rejected 상태: 실패한 상태.

Promise의 인스턴스가 만들어지면 해당 로직은 pending 상태가 된다.

new Promise();


또한 콜백 함수의 인자로 resolve와 reject를 사용하는데 이는 각각 이행상태와 실패상태를 의미한다.


function getData() {
  return new Promise(function (resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function (resolvedData) {
  console.log(resolvedData); // 100
});

function getData() {
  return new Promise(function (resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function (err) {
  console.log(err); // Error: Request is failed
});


예시들

new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})
.then(function(result) {
  console.log(result); // 1
  return result + 10;
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

getData(userInfo)
  .then(parseValue)
  .then(auth)
  .then(diaplay);

var userInfo = {
  id: 'test@abc.com',
  pw: '****'
};

function parseValue() {
  return new Promise({
    // ...
  });
}
function auth() {
  return new Promise({
    // ...
  });
}
function display() {
  return new Promise({
    // ...
  });
}


에러 캐치는 다음과 같이:


// catch()로 오류를 감지하는 코드
function getData() {
  return new Promise(function (resolve, reject) {
    resolve('hi');
  });
}

getData().then(function (result) {
  console.log(result); // hi
  throw new Error("Error in then()");
}).catch(function (err) {
  console.log('then error : ', err); // then error :  Error: Error in then()
});


주의점 resolve가 return을 의미하는것은 아니기 때문에 함수의 종결을 의미하는 것은 아니다.

3. async & await

promise 객체를 반환하는 함수를 이용하여 .then과 같이 사용하지 않아도 된다.

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

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


예제
function fetchUser() {
  var url = 'https://jsonplaceholder.typicode.com/users/1'
  return fetch(url).then(function(response) {
    return response.json();
  });
}

function fetchTodo() {
  var url = 'https://jsonplaceholder.typicode.com/todos/1';
  return fetch(url).then(function(response) {
    return response.json();
  });
}

다음과 같은 코드에서 두 함수는 promise 객체를 반환하게 된다.

따라서 다음과 같이 사용이 가능하다.

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

댓글

가장 많이 본 글