TIL

2024년 12월 9일 TIL

kagan-draca 2024. 12. 13. 18:05

JavaScript map() async 콜백 함수

Item Class

class Item {
	...
	async isItemUsable(item, user) {
    // 스팀팩 사용 가능 여부 확인 해주는 조건문
    if (this.items[item.itemId - BASE_ITEM_ID_OFFSET].itemId === STIMPACK_POTION_ID) {
      // 스팀팩(광포화 포션)
      return (
        !user.stat.stimPack &&
        this.items[item.itemId - BASE_ITEM_ID_OFFSET].count > 0 &&
        user.stat.hp > 50
      );
    }
    // 다른 아이템들은 개수가 0 이상이면, 사용 가능
    return item.count > 0;
  }
  ...
 }

export default Item;

 

내부 async 메서드를 JavaScript의 아래와 같이 map 함수를 사용해

 

const buttons = items.map(async(item) => ({
	msg: ${itemsName[item.itemId - BASE_ITEM_ID_OFFSET]}(보유 수량: ${item.count}),
	enable: await this.user.inventory.isItemUsable(item, this.user),
}));

 

새로운 배열을 반환 받고자 시도할 경우

 

 

위와 같이 enable(key) 값에 Promise { false }(value)라는 값이 저장되는 문제가 존재했다.

이유로는 map의 콜백 함수를

 

async(item) => ({
	enable: await this.user.inventory.isItemUsable(item, this.user)
});

 

async로 처리 할 경우, map 내부에서 각 호출 결과를 Promise로 처리하기 때문이었다.

 

이러한, 문제는 JavaScript 내장 함수를 비동기 처리할 때 동작 원리를

정확하게 이해하지 못 한 상황에서 발생한 오류였고, 이를 해결하기 위해서는

    const buttons = await Promise.all(
      items.map(async (item) => ({
        msg: `${itemsName[item.itemId - BASE_ITEM_ID_OFFSET]}(보유 수량: ${item.count})`,
        enable: await this.mover.inventory.isItemUsable(item, this.mover),
      })),
    );

    buttons.push({ msg: BUTTON_BACK, enable: true });

위와 같이 await Promise.all()로 해당 코드를 감싸줄 경우 해결할 수 있었다.

 

Promise.all()로 해결된 이유는 모든 작업을 병렬로 처리해 모든 병렬 작업이 완료될 때까지

대기 및 완료된 후 결과를 반환해주기 때문이었다.

 

이러한, Promise.all()로 병렬처리를 수행할 경우, 이점은

순차적으로 처리하는 것 보다 속도가 훨씬 빨라지고, 하나라도 예외가 발생할 경우

reject로 오류를 일괄적으로 처리할 수 있다는 장점이 존재했다.