비동기 처리 호출에 쓰이는 문법

34606 단어 JavaScriptJavaScript



웹 사이트 상에서 비동기 흐름이 없을 경우 어떻게 될까??

영상에서 버퍼링이 걸리는 동안 댓글, 좋아요, 구독, 채팅, 알림, 썸네일 미리보는 이벤트 등등

다양한 서비스를 동시 진행을 못하게 되므로 너무 정적인 페이지같은 모습으로 인해 이용하기에 불편해질 것이다.

이러한 단점을 보완하고자 버퍼링이 되거나 말거나 다른 서비스 이벤트를 마구 활용할 수 있게 해주는 것이

바로 오늘 소개할 비동기 호출이다.

비동기 호출을 활용하는데 쓰이는 문법은 3가지가 있다.

  1. callback
  2. Promise
  3. async, await

오늘은 각 문법 간의 특징과 사용법에 대해 알아보자






Callback


function loginSystem (id, password, onSuccess, onError) {
    setTimeout( () => {
        if (
            (id === 'cali' && password === 'fornia')
        ) {
            onSuccess("로그인에 성공하셨습니다.")           
        } else {
            onError(new Error("비밀번호가 틀립니다."))
        }
    }, 2000)
}

let getId = prompt("")
let getPassword = prompt("")

loginSystem(
getId, 
getPassword, 
(data) => {
  alert(data)}, 
(error) => {
  console.log(error)}
)

웹 사이트 상에서 로그인을 구현할때 쓰이는 코드를 구현하였다.

  1. 아이디와 비밀번호가 일치한지 판별해주는 loginSystem은 호출문에서 getId, getPassword
    입력을 인자로 받는다.
  2. setTimeout 메서드를 통해 로그인이 성공하면 onSuccess, 실패하면 onError 실행
  3. 각 조건에 따라 2초 뒤 결과가 최종 호출

loginSystem에서 직접적으로 콜백함수를 생성하여 비동기 호출을 실행해보았다.

위 코드만 보면 callback 문법이 코드 해석상 큰 문제가 없을 수 있지만, 큰 어려움이 봉착될 경우도 있다.

콜백지옥

setTimeout(
  (data) => {
    let word = data;
    console.log(word);

    setTimeout(
      (data) => {
        word = word + data;
        console.log(word);

        setTimeout(
          (data) => {
            word = word + data;
            console.log(word);

            setTimeout(
              (data) => {
                word = word + data;
                console.log(word);
              },
              1000,
              '옥',
            );
          },
          1000,
          '지',
        );
      },
      1000,
      '백',
    );
  },
  1000,
  '콜',
);

1초마다 "콜백지옥"이 쌓이는 과정인 코드를 보면 너무 가독성에 악영향을 끼치는 것을 볼 수가 있다.

위와 같은 콜백지옥을 해결해주는 문법이 바로 Promise, async, await이다!!!






Promise


new Promise((resolve) => {
  setTimeout(() => {
    let name = '프';
    console.log(name);
    resolve(name);
  }, 500);
})
  .then((data) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        let name = data + '로';
        console.log(name);
        resolve(name);
      }, 500);
    });
  })
  .then((data) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        let name = data + '미';
        console.log(name);
        resolve(name);
      }, 500);
    });
  })
  .then((data) => {
    return new Promise((resolve) => {
      setTimeout(() => {
        let name = data + '스';
        console.log(name);
        resolve(name);
      }, 500);
    });
  });

Promise는 자바스크립트 비동기 처리에 가장 많이 사용되며, 주로 서버 측에서 응답을 받아 데이터자원을

브라우저 상에서 렌더링되도록 할 때 쓰인다.

콜백함수와 다른점은 코드줄이 규칙적이며, then 메서드를 통해 여러개의 프로미스 데이터를 연결할수 있다.

then 메서드를 계속 호출할수록 새로운 프로미스 객체가 반환되는데, 콜백함수는 콜백을 받고 다음 콜백으로 보내줄 필요가 없어 데이터를 바로 다음 함수로 실행할 수 있어 훨씬 편하다.

Promise.all


let los_angeles = "https://api.weather.com/cailfornia/LA"
let new_york = "https://api.weather.com/newyork/NY"
let sanfrancisco = "https://api.weather.com/cailfornia/SF"

let total = [
  				fetch(los_angeles), 
  				fetch(new_york), 
  				fetch(sanfrancisco)
			]


Promise.all(total)
	.then((data) => {
		return Promise.all(data.map(el) => {
			return el.json()
		})
	})
	.then((data) => {
  		return {LA = data[1], NY = data[1], SF = data[2]}
	})

만일 프로미스를 자주 활용해야할 상황이 있을 경우 Promise.all을 활용하면 한번에 묶어서

then 메서드의 다음 데이터로 전달할 수가 있다.

두 세번 넘게 반복할 작업을 한번에 배열로 묶는 관계로 코드 가독성에 더 효율적인 효과를 준다.

순서에 따른 특징


Promise.all([
  new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
  new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
  new Promise(resolve => setTimeout(() => resolve(3), 1000))  // 3
]).then((data) => console.log(data))

// [1, 2, 3]

전체 프로미스에서 첫번째 프로미스가 가장 늦게 실행되어도 처리 결과는 무조건 배열의 첫 번째 요소에 저장된다.

전체 요소에 따른 결과


Promise.all([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("error!")), 2000)), // 2
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000))  // 3
]).then((data) => console.log(data))

// VM1043:3 Uncaught (in promise) Error: error!
//    at <anonymous>:3:60

전체 프로미스 중 하나라도 에러가 발생할 경우, 즉시 반환하는 프로미스는 에러가 출력된다.






async, await



let arr = [1,2,3];


function func(item) {
  return new Promise(function(resolve, reject) {
    if(item) {
		resolve(item)
	} else {
		reject(console.error("Error!!!"))
	}
  });
}


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

ES7부터 async/await 비동기 호출 처리 언어가 새로 등장하였다.

기존의 프로미스 비동기 호출과 달리 더 간결하고 가독성이 편리해졌으며, async는 함수명을 적어주고

await은 비동기 처리 메서드를 적으면 프로미스 형태로 반환하게 된다.

await은 일종의 정지 기능이 있어 만일 해당 코드가 await 과정을 걷는다면 잠시 멈추고 끝이나면

바로 다음 코드로 넘어가며 동기적 처리 기능 성향을 지니고 있다.

만일 await을 안붙이고 진행한다면 데이터를 받기전 바로 밑의 코드로 넘어가 출력되어 undefiend가 발생한다.

Promise와 차이는??


let arr = [1,2,3];


function func(item) {
  return new Promise(function(resolve, reject) {
    if(item) {
		resolve(item)
	} else {
		reject("Error!!!")
	}
  });
}


func(arr)
.then(data => console.log(data))
.catch(err => console.error(err))

위 섹션의 async/await 코드와 달리 Promise는 then(), catch() 메서드를 사용하여야만

비동기적 처리 실행이 되므로 두 개의 문법 간 차이가 있다.

좋은 웹페이지 즐겨찾기