TIL - 6회차

원종현·2021년 3월 11일
0

TIL

목록 보기
6/7
post-thumbnail

2021.03.11

공부한 내용


  • Promise API
  • async/await
  • JSON

Promise API


Promise클래스에는 5가지 정적 메서드가 존재한다.

Promise.all


let promise = Promise.all([...promises...]);

Promise.all은 요소 전체가 프라미스인 배열을 받고 새로운 프라미스를 반환한다.
배열 안에 프라미스들이 모두 처리되면 새로운 프라미스가 이행되는데, 배열 안 프라미스들의 결과값을 담은 배열이 새로운 프라미스의 result가 된다.

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3]).then(values => {
  console.log(values); // Array [1, 2, 3]
});

새로운 프라미스의 result 배열의 요소 순서는 Promise.all에 전달되는 프라미스 순서와 상응한다.

첫 번째 프라미스가 가장 늦게 이행되더라도 처리 결과는 배열의 첫 번째 요소에 저장된다.

실사용 예시 모던자바스크립트 사이트에서 가져왔습니다.

let names = ['iliakan', 'remy', 'jeresig'];

let requests = names.map(name => fetch(`https://api.github.com/users/${name}`));

Promise.all(requests)
  .then(responses => {
    // 모든 응답이 성공적으로 이행되었습니다.
    for(let response of responses) {
      alert(`${response.url}: ${response.status}`); // 모든 url의 응답코드가 200입니다.
    }

    return responses;
  })
  // 응답 메시지가 담긴 배열을 response.json()로 매핑해, 내용을 읽습니다.
  .then(responses => Promise.all(responses.map(r => r.json())))
  // JSON 형태의 응답 메시지는 파싱 되어 배열 'users'에 저장됩니다.
  .then(users => users.forEach(user => alert(user.name)));

기타 정보!

  • Promise.all에 전달되는 프라미스 중 하나라도 거부되면, Promise.all이 반환하는 프라미스는 에러와 함께 거부된다.
    하나라도 거부되면 배열에 저장된 다른 프라미스의 결과도 잊혀진다 이행된 결과도 무시된다.
  • 배열의 모든 요소가 Promise 인스턴스일 필요는 없다.
    프라미스가 아닌 객체가 배열로 들어가면, 요소가 그대로 결과 배열로 전달된다.

Promise.allSettled


최근에 추가된 문법!! -> 구식 브라우저를 위한 폴리필 필요

Promise.all같은 경우 프라미스가 하나라도 거절되면 전체가 거절되지만 Promise.allSettled은 여러 요청 중 하나가 실패해도 다른 요청의 결과는 여전히 존재해야하는 상황에서 사용할 수 있다.

Promise.allSettled에서 반환되는 배열

  • 응답이 성공할 경우 => {status:"fulfilled", value:result}
  • 에러가 발생한 경우 => {status:"rejected", reason:error}

예제 모던자바스크립트 사이트에서 가져왔습니다.

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://no-such-url'
];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => { 
    results.forEach((result, num) => {
      if (result.status == "fulfilled") {
        alert(`${urls[num]}: ${result.value.status}`);
      }
      if (result.status == "rejected") {
        alert(`${urls[num]}: ${result.reason}`);
      }
    });
  });

//result
[
  {status: 'fulfilled', value: ...응답...},
  {status: 'fulfilled', value: ...응답...},
  {status: 'rejected', reason: ...에러 객체...}
]

Promise.race


Promise.racePromise.all과 비슷하다.
하지만 Promise.race는 가장 머저 처리되는 프라미스의 결과를 반환한다.

Promise.race([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error("에러 발생!")), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(alert); // 1

첫 번째 프라미스가 가장 빨리 처리상태가 되기 때문에 첫 번째 프라미스의 결과인 1이 반환된다. 가장 빨리 처리된 프라미스가 나타난 순간 다른 프라미스의 결과 또는 에러는 무시된다.

Promise.resolve/reject


Promise.resolve, Promise.rejectasync/await문법이 생긴 후 근래에는 거의 사용하지 않음

Promise.resolve

Promise.resolve(value)는 결과값이 value인 성공 상태의 프라미스를 생성한다.

let promise = new Promise(resolve => resolve(value));

Promise.resolve는 호환성을 위해 함수가 프라미스를 반환하도록 해야 할 때 사용한다.

함수의 반환값(return값)으로 Promise.resolve()를 사용하면 그 함수를 호출하면 프라미스가 반환된다는 것이 보장되기 때문에
function().then(..)을 사용할 수 있다.

function A(value) {
  if(value) return Promise.resolve(value);
}
A(value).then(....);

Promise.reject

Promise.reject(error)는 결과값이 error인 거부 상태 프라미스를 생성한다.

let promise = new Promise((resolve, reject) => reject(error));

Async/Await


ES2017에 새롭게 등장한 자바스크립트 비동기 처리 문법

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

비동기 처리 메서드가 꼭 프로미스 객체를 반환해야 await가 의도한 대로 동작한다. 일반적으로 await의 대상이 되는 비동기 처리 코드는 Axios등 프라미스를 반환하는 API 호출 함수이다.

async/await란 무엇인가?


Promise를 기반으로 사용되는 비동기 코드를 좀 더 동기적인 코드처럼 작성할 수 있게 해주는 문법이다. 기존의 비동기 처리 방식인 콜백 함수와 프라미스의 단점을 보완해준다.

async/ awaitPromise를 대체하는 것이 아니라는 것을 유념해야 한다.
Promise를 사용하지만 then, catch 메서드를 사용하여 컨트롤 하는 것이 아닌 동기적 코드처럼 반환 값을 변수에 할당하여 작성할 수 있게끔 도와주는 문법이다.

async 함수


async는 function앞에 위치한다.

async function f() {
  return 1;
}

function앞에 async키워드를 붙이면 그 함수는 항상 프라미스를 반환한다. 프라미스가 아닌 값을 반환하더라도 성공(resolve) 상태의 프라미스로 값을 감싼 성공 프라미스가 반환되도록 한다.
즉, async가 붙은 함수는 반드시 프라미스를 반환하고, 프라미스가 아닌 것은 프라미스로 감싸서 반환한다.

반드시 Promise를 반환하므로 .then, .catch메서드 사용 가능

await


awaitasync함수 안에서만 동작한다.

// await는 async 함수 안에서만 동작합니다.
let value = await promise;

자바스크립트는 await키워드를 만나면 프라미스가 처리(Settled)상태가 될 때까지 기다린다. 결과는 그 이후에 반환!!

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("완료!"), 1000)
  });

  let result = await promise; // 프라미스가 이행될 때까지 기다림

  alert(result); // "완료!"
}

f();

이렇게 함수를 호출하면 await구문에서 Promise가 처리될 때까지 실행이 잠시 중단되었다가 프라미스가 처리 완료되면 실행이 재개된다. ->1초 뒤에 "완료!"가 출력된다.

await는 말 그대로 프라미스가 처리될 때까지 함수 실행을 기다리게 만든다. 프라미스가 처리되면 그 결과와 함께 실행이 재개된다. 프라미스가 처리되길 기다리는 동안엔 엔진이 다른 일(다른 스크립트를 실행, 이벤트 처리 등)을 할 수 있기 때문에, CPU 리소스가 낭비되지 않는다.

await 키워드는 async 함수에서만 사용 가능하며 일반 함수에는 사용할 수 없다.
async 함수가 아닌 곳에서 사용하면 SyntaxError가 발생된다.

async/await 실사용 예제


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

이렇게 여러 개의 비동기 처리 코드를 다룰 때 async/await문법을 많이 사용한다. 콜백이나 프라미스보다 훨씬 더 깔끔하게 코드를 작성할 수 있다.

async 클래스 메서드

메서드 앞에 async키워드를 추가하면 async 클래스 메서드를 선언할 수 있다.

class Waiter {
  async wait() {
    return await Promise.resolve(1);
  }
}

new Waiter()
  .wait()
  .then(alert); // 1

async메서드와 async함수는 프라미스를 반환하고 await를 사용할 수 있다는 공통점이 있다.

에러 핸들링


프라미스가 정상적으로 이행되면 await promise는 프라미스 객체의 result에 저장된 값을 반환한다. 반면 프라미스가 거부되면 마치 throw문을 작성한 것처럼 에러가 던져진다.

async function f() {
  await Promise.reject(new Error("에러 발생!"));
}
//같은 코드
async function f() {
  throw new Error("에러 발생!");
}

async/await에서 예외 처리 방법은 try...catch를 사용한다.

async function logTodoTitle() {
  try {
    var user = await fetchUser();
    if (user.id === 1) {
      var todo = await fetchTodo();
      console.log(todo.title); 
    }
  } catch (error) {
    console.log(error);
  }
}
  • 위 코드처럼 async에서는 catch{}를 사용하면 된다. 에러가 발생하면 제어 흐름이 catch{}블록으로 넘어간다.
  • 여러 줄의 코드를 try로 감싸는 것도 가능하다.
  • 네트워크 통신 오류뿐만 아니라 간단한 타입 오류 등 일반적인 에러까지도 catch로 잡아낼 수 있다.
  • 에러는 error객체에 담겨 발생한 에러의 유형에 맞게 에러 코드를 처리해 준다.

try...catch가 없으면 async함수에 .catch()를 추가해서 처리해 준다.

async function f() {
  let response = await fetch('http://유효하지-않은-url');
}

// f()는 거부 상태의 프라미스가 됩니다.
f().catch(alert); // TypeError: failed to fetch

기타


console.log(0);

function promise() {
  console.log(1);
  
  return new Promise(resolve => {
    setTimeout(() => {
      
      console.log(2);
      resolve('resolved');
    }, 2000);
  });
}

console.log(3);

async function asyncCall() {
  try {
      console.log(4);
    
      const result = await promise(); // Promise가 settled될 때까지 기다린 후 resolve된 값을 할당한다.
    
      console.log(result); 
      console.log(5);
  } catch(err) {
    console.error(err); // error 발생 시 catch 블락에서 잡히도록 handling
  }
}

console.log(6);

asyncCall();

// 출력 값
0
3
6
4  // asyncCall 호출
1  // promise 함수 호출
2  // 2초 후 setTimeout 콜백 함수 호출
resolved // resolve함수 호출
5  // await 후 다음 코드 실행

비동기 상황에서는 어떤 이벤트가 먼저 완료될 지 순서를 정확하게 알 수 없지만 async/await문법을 사용하면 먼저 완료되어야 하는 이벤트들의 순서대로 (동기적으로 실행되는) 코드를 작성할 수 있다.
위 코드에서 console.log의 출력 순서를 보면 알 수 있다.

실제 코드를 작성할 때에는 axiosfetch를 통해 ajax 요청하면 자동으로 Promise를 반환해주기 때문에 Promise 객체를 직접 생성하는 경우보다 Promise로 반환되는 객체들에 async/await을 사용하여 비동기 처리하는 경우가 대부분이다.

JSON


JSON (JavaScript Object Notation)

  • JavaScript Object Notation라는 의미의 축약어로 데이터를 저장하거나 전송할 때 많이 사용되는 경량의 DATA - 교환 형식이다.
  • Javascript에서 객체를 만들 때 사용하는 표현식을 의미한다.
  • JSON 표현식은 사람과 기계 모두 이해하기 쉬우며 용량이 작아서, 최근에는 JSONXML을 대체해서 데이터 전송 등에 많이 사용된다.
  • JSON은 데이터 포맷일 뿐이며 어떠한 통신 방법도, 프로그래밍 문법도 아닌 단순히 데이터를 표시하는 표현 방법일 뿐이다.
  • 주로 클라이언트(web)와 서버 간에 데이터를 전송 하는데 사용된다.
  • 파일의 확장자는 .json 이다.
  • JSON 인터넷 미디어 타입은 application/json 이다.

특징


  • 서버와 클라이언트 간의 교류에서 일반적으로 많이 사용된다.
  • 자바스크립트 객체 표기법과 아주 유사하다.
  • 자바스크립트를 이용하여 JSON 형식의 문서를 쉽게 자바스크립트 객체로 변환할 수 있는 이점이 있다.
  • JSON 문서 형식은 자바스크립트 객체의 형식을 기반으로 만들어졌다.
  • 자바스크립트의 문법과 굉장히 유사하지만 텍스트 형식일 뿐이다.
  • 다른 프로그래밍 언어를 이용해서도 쉽게 만들 수 있다.
  • 특정 언어에 종속되지 않으며, 대부분의 프로그래밍 언어에서 JSON 포맷의 데이터를 핸들링 할 수 있는 라이브러리를 제공한다.
  • JSON을 지원하는 함수들은 언어마다 존재한다.

문법


  • JSON 형식은 자바스크립트 객체와 마찬가지로 key / value가 존재할 수 있으며 key값이나 문자열은 항상 쌍따옴표를 이용하여 표기해야한다.
  • 객체, 배열 등의 표기를 사용할 수 있다.
  • 일반 자바스크립트의 객체처럼 원하는 만큼 중첩시켜서 사용할 수도 있다.(쉼표 ,로 나열)
  • JSON형식에서는 null, number, string, array, object, boolean을 사용할 수 있다.

형식


  • name - value : 이름과 값의 쌍으로 이루어진다.
{
  "Name": "haha",
  "age": 20,
  "email": "test@gmail.com"
}
  • 리스트 형식 (배열[Array], 객체{Object} 사용 가능)
{
  "animal" : {
   "dog" : [
     {"name":"nana", "age":1},
     {"name":"jo", "age":2},
     {"name":"tia", "age":3}
   ],
   "cat" : [
     {"name":"haha", "age":4},
     {"name":"nabi", "age":5},
     {"name":"saho", "age":6}
   ]
  }
}

메서드


  • JSON.parse( json ) : JSON 형식의 텍스트를 자바스크립트 객체로 변환한다.
  • JSON.stringify( object ) : 자바스크립트 객체를 JSON 텍스트로 변환한다.
profile
프론트엔드 엔지니어를 목표로 공부하는 중입니다!.

0개의 댓글