자바스크립트 비동기 처리

🌊·2022년 1월 17일
0

기술공부

목록 보기
4/4
post-thumbnail

비동기 처리

특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성

비동기 처리 사례

제이쿼리 ajax

보통 화면에 표시할 이미지나 데이터를 서버에서 불러와 표시해야 하는데 이때 ajax 통신으로 데이터를 서버에서 가져올 수 있다.

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

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

ajax 요청을하고 response를 받아온 뒤 tableDatareturn해야 undefined가 아닌 요청한 데이터를 확인할 수 있다.
하지만 ajax 요청을 하고 난 뒤 response를 받아오기 전에 return이 됐다.
위처럼 비동기 처리는 로직을 끝날때 까지 다 기다리지 않고 뒤의 코드를 먼저 실행하는 것이다.

setTimeout()

코드를 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행시키는 API

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

실제 출력은 Hello, Hello Again, 3초 뒤 Bye 순으로 출력된다.
비동기 처리로 이루어지기 때문에 3초를 기다렸다가 Hello Again이 먼저 출력되는 것이 아니라 Bye가 출력되고 3초가 지나면 Hello Again이 출력된다.

콜백(Callback) 함수로 비동기 처리 방식 문제점 해결하기

데이터를 받아오고 그 이후의 로직이 실행되거나, 일정 시간 이후에 진행되어야 하는 로직에 대해서는 비동기 처리 방식이 문제가 될 수 있다.
이러한 문제를 해결할 수 있는 방식이 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에 전달됨
});

response가 생성된 이후에 callback 함수가 실행되기 때문에 tableData를 출력했을 때 undefined가 출력될 일이 없다.

콜백 지옥

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

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

데이터를 받아오거나, 인코딩, 인증 등의 모든 절차를 비동기로 처리해야하면 콜백 안에 콜백이 계속 생성되는 구조가 나타날 수 있다.
가독성도 떨어지고 로직 유지보수도 어렵다.

콜백 지옥 해결 방법

PromiseAsync를 사용하는 방법이 있다. 코딩 패턴만으로 콜백 지옥을 해결하려면 콜백 함수를 분리해주면 된다.

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);
});

하지만 Promise나 Async를 사용하면 더 편하게 콜백 지옥을 해결할 수 있다.

Promise

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

주로 서버에서 받아온 데이터를 화면에 표시할 때 사용한다.

ajax + Promise

function getData(callback) {
  // new Promise() 추가
  return new Promise(function(resolve, reject) {
    $.get('url 주소/products/1', function(response) {
      // 데이터를 받으면 resolve() 호출
      resolve(response);
    });
  });
}

// getData()의 실행이 끝나면 호출되는 then()
getData().then(function(tableData) {
  // resolve()의 결과 값이 여기로 전달됨
  console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
});

프로미스 상태

Pending (대기)

  • 비동기 처리 로직이 아직 완료되지 않은 상태
  • 메서드를 호출한 상태
  • 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject
new Promise();

Fulfilled (이행)

  • 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태 (완료)
  • 콜백함수의 인자 resolve를 실행하면 이행 상태가 된다.
  • 이행 상태가 되면 then()을 이용해서 처리 결과를 받을 수 있다.
function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

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

Rejected (실패)

  • 비동기 처리가 실패하거나 오류가 발생한 상태
  • 실패 상태가 되면 catch()를 이용해서 실패한 이유(실패 처리 결과 값)를 받을 수 있다.
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
});

then()이나 catch()를 호출하고 나면 새로운 Promise 객체가 반환된다.
해당 객체를 통해서 추가적인 then(), catch()가 가능하다.

여러 개의 프로미스 연결 (Promise Chaining)

function getData() {
  return new Promise({
    // ...
  });
}

// then() 으로 여러 개의 프로미스를 연결한 형식
getData()
  .then(function(data) {
    // ...
  })
  .then(function() {
    // ...
  })
  .then(function() {
    // ...
  });

프로미스 에러 처리 방법

  1. then()의 두 번째 인자로 에러를 처리하는 방법
  2. catch()를 이용하는 방법

가급적으로 catch()로 처리하는 것이 더 효율적이다.
then()의 두 번째 인자로 에러를 처리하는 방법보다 더 많은 상황을 예외 처리할 수 있다.

async & await

기존의 비동기 처리 방식인 Callback 함수와 Promise의 단점은 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와준다.

callback 함수를 이용했을 때

function logName() {
  // 아래의 user 변수는 위의 코드와 비교하기 위해 일부러 남겨놓았습니다.
  var user = fetchUser('domain.com/users/1', function(user) {
    if (user.id === 1) {
      console.log(user.name);
    }
  });
}

async & await를 이용했을 때

async function logName() {
  var user = await fetchUser('domain.com/users/1');
  if (user.id === 1) {
    console.log(user.name);
  }
}

함수명 앞에 async를 붙이고, 비동기 처리 코드 앞에 await를 붙인다.
비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작한다.
await의 대상이 되는 비동기 처리 코드는 axios 등 프로미스를 반환하는 API이다.

async & await 예제

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]
}

logItems()에 프로미스를 사용한다면?

fetchItems().then(function(items) {
  var resultItems = items;
  console.log(resultItems);
}

async & await 예제 (2)

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();
  });
}
async function logTodoTitle() {
  var user = await fetchUser();
  if (user.id === 1) {
    var todo = await fetchTodo();
    console.log(todo.title); // delectus aut autem
  }
}

비동기 처리 코드를 콜백이나 프로미스로 했다면 훨씬 코드가 길어졌을 것이다.
async & await 문법을 이용하면 기존의 비동기 처리 코드 방식으로 사고하지 않아도 되는 장점이 생긴다.

async & await 예외 처리

try 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);
  }
}

네트워크 통신 오류뿐만 아니라 간단한 타입 오류 등의 일반적인 오류까지도 catch로 잡아낼 수 있다.
발견된 에러는 error 객체에 담기기 때문에 에러 유형에 맞게 에러 코드를 처리해주면 된다.

출처

자바스크립트 비동기 처리와 콜백 함수
자바스크립트 Promise 쉽게 이해하기
자바스크립트 async와 await

0개의 댓글