배꼽파지 않도록 잘 개발해요

[코드잇] 자바스크립트 웹 개발 기본기 ③ - async/await을 활용한 세련된 비동기 코드 본문

코드잇 Codeit/Front-End

[코드잇] 자바스크립트 웹 개발 기본기 ③ - async/await을 활용한 세련된 비동기 코드

꼽파 2024. 1. 5. 11:12


async/await

async/await 구문은 Syntactic sugar(기존 문법을 더 편하게 사용할 수 있도록 하는 문법적 장치)에 해당

fetch('https://jsonplaceholder.typicode.com/users')
	.then((response) => response.text())
	.then((result) => { console.log(result); });
async function fetchAndPrint() {
	const response = await fetch('https://jsonplaceholder.typicode.com/users');
	const result = await response.text();
	console.log(result);
}

fetchAndPrint();

 

 

await는 프로미스 객체 앞에 리턴하는 코드 앞에 붙임, async 함수 안에서만 사용 가능함.
그 뒤의 코드를 실행하고, 그 코드가 리턴하는 프로미스 객체를 기다려줌.
(해당 객체가 fulfilled 상태 또는 rejected 상태가 될 때까지 기다림)
프로미스 객체가 fulfilled상태가 되면 작업 성공 결과를 추출해서 리턴함.

async가 붙어있는 함수 안에 비동기 실행 되는 부분이 있음 

= 함수 코드 중 프로미스 객체를 리턴하는 코드가 있음.
그 앞에 await를 붙여서 프로미스 객체라 fulfilled 상태가 될 때까지 기다림.
fulfilled 상태가 되고 작업 결과를 리턴하면, 그 다음 줄을 실행함.

 

async function fetchAndPrint() {
	console.log(2);
	const response = await fetch('https://jsonplaceholder.typicode.com/users');
	console.log(7);
	const result = await response.text();
	console.log(result);
}

console.log(1);
fetchAndPrint();
console.log(3);
console.log(4);
console.log(5);
console.log(6);

await을 만나면 일단 그 뒤 코드를 실행하고, 실행 흐름은 현재 함수가 호출되는 fetchAnPrint 함수가 있는 곳으로 감.
fetchAndPrint 바깥으로 가서 console.log(3)부터 쭉 실행됨.
그러고나서 await 뒤에 있는 promise 객체가 fulfilled 상태가 될 때까지 기다림.

 

undefined는 console.log(6)이 아무런 리턴값이 없기 때문에 출력됨.

개발자도구에서는 코드 마지막에 아무런 리턴값이 없으면 undefined가 출력됨.


catch문과 finally문

async function fetchAndPrint() {
	const response = await fetch('https://jsonplaceholder.typicode.commm/users');
	const result = await response.text();
	console.log(result);
}

fetchAndPrint();

 

프로미스 체이닝에서는 맨 마지막에 catch구문을 넣었음.
async/await 구문에서는 try...catch문을 사용

async function fetchAndPrint() {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.commm/users');
        const result = await response.text();
        console.log(result);
    } catch (error) {
    	console.log(error);
    } finally {  // 항상 출력
    	console.log('exit');
    }
}    

fetchAndPrint();


async 함수가 리턴하는 Promise 객체

  상태 결과
리턴하는 값 Promise객체 해당 Promise 객체와 동일한 상태 해당 Promise 객체와 동일한 작업 결과
(성공 또는 실패)
Promise 객체 이외의 값 fullfilled 상태 리턴된 값
아무 값도 리턴하지 않는 경우 fullfilled 상태 undefined
async 함수 내부에서 에러가 발생했을 때 rejected 상태 해당 에러 객체를 작업 실패 정보로 가진 Promise 객체

 

리턴값이 Promise 객체 이외의 값인 경우

async function fetchAndPrint() {
	return 3;
}

fetchAndPrint();

fetchAndPrint 함수는 숫자 3을 리턴 (X)
숫자 3을 작업 성공 결과로 가진 fulfilled 상태의 프로미스 객체를 리턴함. (O)

 

리턴값이 Promise 객체인 경우

async function fetchAndPrint() {
	return fetch('https://jsonplaceholder.typicode.com/users')
		.then((response) => response.text());
}

fetchAndPrint();

리턴된 프로미스 객체의 동일한 작업 성공 결과를 가진 프로미스 객체가 리턴됨.

 

아무런 값도 리턴하지 않는 경우

async function fetchAndPrint() {
	// 비어 있음
}

fetchAndPrint();

 

async 함수 내부에서 에러가 발생했을 때

async function fetchAndPrint() {
	throw new Error('Fail');
}

fetchAndPrint();


async 안 async

jason 파일에 민감한 개인정보가 그대로 들어 있음
→ async 안 async 함수를 사용해서 해결 가능

// 비동기 함수로 사용자 정보에 개인정보 보호 규칙을 적용하는 함수
const applyPrivacyRule = async function (users) {
    // 사용자 배열(users)을 순회하면서 개인정보를 제외한 정보를 새로운 배열에 담아 반환
    const resultWithRuleApplied = users.map((user) => {
        const keys = Object.keys(user);
        const userWithoutPrivateInfo = {};
        keys.forEach((key) => {
            // 'address'와 'phone' 키를 가진 개인정보를 제외한 속성만 userWithoutPrivateInfo에 추가
            if (key !== 'address' && key !== 'phone') {
                userWithoutPrivateInfo[key] = user[key];
            }
        });
        return userWithoutPrivateInfo;
    });

    // 2초 후에 처리 결과를 resolve하는 Promise 생성
    const p = new Promise((resolve, reject) => {
        setTimeout(() => { resolve(resultWithRuleApplied); }, 2000);
    });
    return p;
};

// 사용자 정보를 가져오는 비동기 함수
async function getUsers() {
    try {
        // 외부 API에서 사용자 정보를 가져오는 HTTP 요청
        const response = await fetch('https://jsonplaceholder.typicode.com/users');
        // HTTP 응답을 텍스트로 변환
        const result = await response.text();
        // 텍스트를 JSON으로 파싱하여 사용자 정보 배열로 변환
        const users = JSON.parse(result);
        // 개인정보 보호 규칙을 적용한 결과를 가져오기 위해 applyPrivacyRule 함수 호출
        const resultWithPrivacyRuleApplied = await applyPrivacyRule(users);
        // 최종 결과 반환
        return resultWithPrivacyRuleApplied;
    } catch (error) {
        // 예외가 발생한 경우 콘솔에 에러 메시지 출력
        console.log(error);
    } finally {
        // 예외 발생 여부와 상관없이 항상 실행되는 블록. 'exit' 메시지 출력
        console.log('exit');
    }
}

// getUsers 함수 호출
getUsers();

 

각 URL에 대한 응답을 순차적으로 가져와서 응답 텍스트를 출력

// 각 URL에 대한 응답을 순차적으로 가져와서 응답 텍스트를 기록
async function getResponses(urls) {
  for (const url of urls) {
    // 현재 URL에 대한 응답을 가져오기
    const response = await fetch(url);
    // 응답의 텍스트 내용을 출력
    console.log(await response.text());
  }
}

 

각 URL에 대한 응답을 동시에 가져와서 응답 텍스트를 출력

// 각 URL에 대한 응답을 동시에 가져와서 응답 텍스트를 기록
async function fetchUrls(urls) {
  for (const url of urls) {
    // 즉시 호출되는 함수 표현식 (IIFE)을 사용하여 새로운 비동기적 컨텍스트를 생성
    (async () => {
      // 현재 URL에 대한 응답을 가져오기
      const response = await fetch(url);
      // 응답의 텍스트 내용을 출력
      console.log(await response.text());
    })();
  }
}

 

  • 콜백을 함수의 파라미터로 바로 전달하는 전통적인 방식의 비동기 실행 함수들 중에서도 setInterval, addEventListener처럼 그 콜백이 단 한번이 아니라 여러 번 실행되어야 하는 것들은 Promisify해서 사용하면 안 됨.
  • Promise 객체는 한번 fulfilled 또는 rejected 상태가 되고나면 그 상태와 결과가 다시는 바뀌지 않기 때문임.

 

  • async/await 구문의 경우, await은 async 함수 안에서만 사용할 수 있고, 코드의 top-level(어떤 함수 블록 안에 포함된 것이 아닌 코드의 최상위 영역)에서는 사용될 수는 없음
  • 그래서 코드의 top-level에서 async 함수가 리턴한 Promise 객체의 작업 성공 결과를 가져오려면 await을 사용할 수는 없고, 여전히 then 메소드를 사용해야함.

 

https://compat-table.github.io/compat-table/es6/

 

ECMAScript 6 compatibility table

Sort by Engine types Features Flagged features Show obsolete platforms Show unstable platforms <!-- --> V8 SpiderMonkey JavaScriptCore Chakra Carakan KJS Other ⬤ Minor difference (1 point) ⬤ Small feature (2 points) ⬤ Medium feature (4 points) ⬤ La

compat-table.github.io

728x90