Promise, async, await

유아현·2023년 1월 23일
0

JavaScript

목록 보기
25/25
post-thumbnail

📌 Promise

✨ new Promise

Promise는 class이기 때문에 new 키워드를 통해 Promise 객체를 생성한다. 또한 Promise는 비동기 처리를 수행할 콜백 함수(executor)를 인수로 전달받는데 이 콜백 함수는 resolve, reject 함수를 인수로 전달받는다.

Promise 객체가 생성되면 executor는 자동으로 실행되고 작성했던 코드들이 작동된다. 코드가 정상적으로 처리가 되었다면 resolve 함수를 호출하고 에러가 발생했을 경우에는 reject 함수를 호출하면 된다.

let promise = new Promise((resolve, reject) => {
	// 1. 정상적으로 처리되는 경우
	// resolve의 인자에 값을 전달할 수도 있습니다.
	resolve(value);

	// 2. 에러가 발생하는 경우
	// reject의 인자에 에러메세지를 전달할 수도 있습니다.
	reject(error);
});

✨ Promise 객체의 내부 프로퍼티

new Promise가 반환하는 Promise 객체는 state, result 내부 프로퍼티를 갖는다. 하지만 직접 접근할 수 없고 .then, .catch, .finally 의 메서드를 사용해야 접근이 가능하다.

✨ State

기본 상태는 pending(대기) 입니다. 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동했다면 fulfilled (이행)로 변경이 되고, 에러가 발생했다면 rejected (거부)가 된다.

✨ Result

처음은 undefined 이다. 비동기 처리를 수행할 콜백 함수(executor)가 성공적으로 작동하여resolve(value)가 호출되면 value로, 에러가 발생하여 reject(error)가 호출되면 error로 변한다.

📌 then, catch, finally

✨ Then

executor에 작성했던 코드들이 정상적으로 처리가 되었다면 resolve 함수를 호출하고 .then 메서드로 접근할 수 있다. 또한 .then 안에서 리턴한 값이 Promise면 Promise의 내부 프로퍼티 result를 다음 .then 의 콜백 함수의 인자로 받아오고, Promise가 아니라면 리턴한 값을 .then 의 콜백 함수의 인자로 받아올 수 있다.

let promise = new Promise((resolve, reject) => {
	resolve("성공");
});

promise.then(value => {
	console.log(value);
	// "성공"
})

✨ Catch

executor에 작성했던 코드들이 에러가 발생했을 경우에는 reject 함수를 호출하고 .catch 메서드로 접근할 수 있다.

let promise = new Promise(function(resolve, reject) {
	reject(new Error("에러"))
});

promise.catch(error => {
	console.log(error);
	// Error: 에러
})

✨ Finally

executor에 작성했던 코드들의 정상 처리 여부와 상관없이 .finally 메서드로 접근할 수 있다.

let promise = new Promise(function(resolve, reject) {
	resolve("성공");
});

promise
.then(value => {
	console.log(value);
	// "성공"
})
.catch(error => {
	console.log(error);
})
.finally(() => {
	console.log("성공이든 실패든 작동!");
	// "성공이든 실패든 작동!"
})

✨ Promise chaining

Promise chaining가 필요하는 경우는 비동기 작업을 순차적으로 진행해야 하는 경우이다. Promise chaining이 가능한 이유는 .then, .catch, .finally 의 메서드들은 Promise를 반환하기 때문이다. 따라서 .then을 통해 연결할 수 있고, 에러가 발생할 경우 .catch 로 처리하면 된다.

let promise = new Promise(function(resolve, reject) {
	resolve('성공');
	...
});

promise
  .then((value) => {
    console.log(value);
    return '성공';
  })
  .then((value) => {
    console.log(value);
    return '성공';
  })
  .then((value) => {
    console.log(value);
    return '성공';
  })
  .catch((error) => {
    console.log(error);
    return '실패';
  })
  .finally(() => {
    console.log('성공이든 실패든 작동!');
  });

✨ Promise.all()

const promiseOne = () => new Promise((resolve, reject) => setTimeout(() => resolve('1초'), 1000));
const promiseTwo = () => new Promise((resolve, reject) => setTimeout(() => resolve('2초'), 2000));
const promiseThree = () => new Promise((resolve, reject) => setTimeout(() => resolve('3초'), 3000));

Promise.all()은 여러 개의 비동기 작업을 동시에 처리하고 싶을때 사용한다. 인자로는 배열을 받는다. 해당 배열에 있는 모든 Promise에서 executor 내 작성했던 코드들이 정상적으로 처리가 되었다면 결과를 배열에 저장해 새로운 Promise를 반환 해준다.

앞서 배운 Promise chaining을 사용했을 경우는 코드들이 순차적으로 동작되기 때문에 총 6초의 시간이 걸리게 된다. 또한, 같은 코드가 중복되는 현상도 발생하게 된다.

// 기존
const result = [];
promiseOne()
  .then(value => {
    result.push(value);
    return promiseTwo();
  })
  .then(value => {
    result.push(value);
    return promiseThree();
  })
  .then(value => {
    result.push(value);
   console.log(result);  
	 // ['1초', '2초', '3초']
  })

이러한 문제들을 Promise.all()을 통해 해결할 수 있다. Promise.all()은 비동기 작업들을 동시에 처리한다. 따라서 3초 안에 모든 작업이 종료된다. 또한 Promise chaining로 작성한 코드보다 간결해진 것을 확인할 수 있다.

// promise.all
Promise.all([promiseOne(), promiseTwo(), promiseThree()])
  .then((value) => console.log(value))
  // ['1초', '2초', '3초']
  .catch((err) => console.log(err));

추가적으로 Promise.all은 인자로 받는 배열에 있는 Promise 중 하나라도 에러가 발생하게 되면 나머지 Promise의 state와 상관없이 즉시 종료된다. 아래의 예시와 같이 1초 후에 에러가 발생하고 Error: 에러1이 반환된 후로는 더 이상 작동하지 않고 종료된다.

Promise.all([
	new Promise((resolve, reject) => setTimeout(() => reject(new Error('에러1'))), 1000),
	new Promise((resolve, reject) => setTimeout(() => reject(new Error('에러2'))), 2000),
	new Promise((resolve, reject) => setTimeout(() => reject(new Error('에러3'))), 3000),
])
	.then((value) => console.log(value))
  .catch((err) => console.log(err));
	// Error: 에러1

✨ async ⇒ 함수를 비동기 함수로 선언하는 키워드

async function hello() {
	return 'Hey guy!';
}

함수를 비동기 함수로 선언하면 함수는 자동으로 Promise를 반환한다.

Promise로 반환하라고 하지 않아도 자동으로 암시한다.

async function hello() {
}

위의 코드와 같이 hello 라는 비동기 함수로 선언하여 return은 물론 아무것도 작성해 주지 않은 상태에서 hello() 호출 시켰을 때, 아래와 같이 자동으로 Promise가 뜨는 것을 확인할 수 있다.

이는, 앞서 말한 것과 같이 함수는 자동으로 Promise를 반환한다는 것을 증명한다.

const login = async (username, password) => {
  if (!username || !password) throw '걸어봐 위엄 랔 어 라이옹...';
  if (password === 'antititi') return '안티티티티!';
  throw '걸어봐 위엄 랔 어 라이옹...';
};

login('user', 'antititi')
  .then((msg) => {
    console.log(`WELCOME! ${msg}`);
  })
  .catch((e) => {
    console.log(`실패... ${e}`);
  });

const login = async (username, password) => {
  if (!username || !password) throw '걸어봐 위엄 랔 어 라이옹...';
  if (password === 'antititi') return '안티티티티!';
  throw '걸어봐 위엄 랔 어 라이옹...';
};

login('user', 'anti')
  .then((msg) => {
    console.log(`WELCOME! ${msg}`);
  })
  .catch((e) => {
    console.log(`실패... ${e}`);
  });

✨ await ⇒ 비동기 코드를 쓰면서 동기적으로 보이게 해 준다.

기다리게 하는 것! Promise가 값을 반환할 때까지 기다리기 위해 비동기 함수의 실행을 일시 정지 시킨다. 비동기 함수에서 적용하기 때문에 async와 await는 한쌍이다!

0개의 댓글