логотип PurpleSchool
Иконка входа
Вход
логотип PurpleSchool

Когда использовать return await?

Разберёмся, что даёт нам запись return await и когда она действительно нужна, а когда создаёт дополнительную сложность в коде. Я очень часто во время ревью встречаю такую конструкцию:

async function getUserById(id: number) {
    //...
    return await db.find(id);
}

Чуть выше в каком-нибудь контроллере мы вызываем эту функцию:

const user = await getUserById(1);

Давайте разберём когда нужно использовать такую запись с await, а когда

async function getUserById(id: number) {
    //...
    return db.find(id);
}

Что происходит?

Когда мы делаем return await мы с вами фактически создаём ещё один Promise. То есть цепочка становится длиннее на один Promise. Сначала будет resolve получения пользователя, а затем уже resolve функции getUserById. В целом на производительность и потребление памяти это почти не влияет, но зачем создавать новый Promise, если мы можем вернуть его на верхний уровень. Мы же хотим оптимальный код 🙂.

На самом деле результат выполнения будет идентичен в обоих случаях.

Когда нужен return await?

Единственный случай, когда нам пригодится return await - это обработка ошибок в самой функции. Давайте перепишем нашу функцию следующим образом:

async function getUserById(id: number) {
    try {
        //...
        return db.find(id);
    } catch(e) {
        // Что-то залогировать или обработать
        // !!! Сюда мы никогда не попадём
        throw e;
    }
}

В это случаем мы хотим обработать ошибку ещё на уровне функции получения пользователя. Но без добавления await мы никогда не попадём в блок catch, так как reject будет происходить уже на уровне выше. Это происходит потому, что мы просто вернули Promise без ожидания результата. И там уже вверху обработчик должен поймать ошибку, которая возникнет у нас в this.db.find(id).

А вот в примере с return await мы будем успешно попадать в блок обработки ошибки:

async function getUserById(id: number) {
    try {
        //...
        return await db.find(id);
    } catch(e) {
        // Что-то залогировать или обработать
        // Если ошибка, то мы сюда попадём
        throw e;
    }
}

Дальше в блоке обработки вы можете прокинуть вверх ошибку как указано в примере или вернуть какой-то дефолтный / нулевой результат на ваше усмотрение.

Итог

Я бы предложил придерживаться следующих правил:

  • Если вам нужно обработать ошибку на том уровне, где происходит возврат Promise - смело используйте return await.
  • Если же нет, просто делайте return, чтобы не создавать дополнительных Promise.

Карта развития разработчика

Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile

Комментарии

1
Иконка аватара

await давно уже не создаёт дополнительный промис под капотом

https://v8.dev/blog/fast-async

Карта развития разработчика

Получите полную карту развития разработчика по всем направлениям: frontend, backend, devops, mobile