async и await и promise в отношении перехвата ошибок

У меня есть вопрос об отлове ошибки пользователя в асинхронном режиме и ожидании.

Допустим, у меня есть маршрут, который извлекается только для одного пользователя.

маршруты.js

routes.get('/getuserbyid/:id', (req, res) => {

    const id = req.params.id;

    accountController.getById(id)
        .then((result) => {
            res.json({
                confirmation: 'success',
                result: result
            });
        })
        .catch((error) => {
            res.json({
                confirmation: 'failure',
                error: error
            });
        });
});

У меня есть контроллер, который получает запрос. accountController.js

export const getById = async (id) => {

        try {
            const user = await users.findOne({ where: {
                    id: id
                }});

            if (user === null) {
                return 'User does not exist';
            }
            return user;
        } catch (error) {
            return error;
        }
}

Поэтому, что бы ни случилось, я получаю либо нулевую, либо одну запись. Это все еще успех. В промисах я могу отклонить null, чтобы он отображался в блоке catch на маршруте. Теперь что с асинхронностью и ожиданием. Как я могу добиться того же, сделав нуль в блоке ошибок?

export const getById = (id) => {

    return new Promise((resolve, reject) => {

        users.findOne({ where: {
            id: id
        }})
            .then((result) => {

                if (result === null) {
                    reject('User does not exit');
                }
                resolve(result);
            })
            .catch((error) => {
                reject(error);
            });
    });

}

person Pak Chu    schedule 08.02.2018    source источник
comment
Вы бы throw null;   -  person Aluan Haddad    schedule 08.02.2018
comment
Я вижу здесь пару проблем. Во-первых, в строгом тесте (user === null), вероятно, отсутствуют некоторые случаи ошибок, например undefined. Во-вторых, try/catch не нужен и может исчезнуть. Если user является чем-то другим, кроме действительного пользовательского объекта, просто выдайте ошибку прямо здесь и сейчас. Это будет обработано вашим обратным вызовом .catch() выше.   -  person greim    schedule 08.02.2018


Ответы (3)


Во-первых, избегайте антипаттерна Promise. У вас есть функция, которая возвращает обещание, не нужно оборачивать ее в new Promise().

Тогда ваша функция может выглядеть так:

export const getById = (id) => {
  return users.findOne({ where: { id } })
    .then((user) => {
      if (user === null)
        throw 'User does not exit';

      return user;
    });
}

и асинхронная/ожидающая версия этого

export const getById = async (id) => {
  const user = await users.findOne({ where: { id } });

  if(user === null)
    throw 'User does not exist';

  return user;
}
person Thomas    schedule 08.02.2018
comment
Спасибо! Это именно то, что мне было нужно. - person Pak Chu; 09.02.2018

Функция async всегда возвращает Promise.

Использование конструкции throw в функции async отклоняет возвращаемый Promise.

Рассмотреть возможность

function getValue() {
  return Promise.reject(null);
}

getValue().catch(e => {
  console.log('An raised by `getValue` was caught by a using the `.catch` method');
  console.log(e);
});


(async function main() {
  try {
    await getValue();
  } catch (e) {
    console.log('An raised by `getValue` was caught by a catch block');
    console.log(e);
  }
}());

async function getValue() {
  throw null;
}

getValue().catch(e => {
  console.log('An raised by `getValue` was caught by a using the `.catch` method');
  console.log(e);
}); 

(async function main() {
  try {
    await getValue();
  } catch (e) {
    console.log('An raised by `getValue` was caught by a catch block');
    console.log(e);
  }
}());

Блок try в асинхронном методе обрабатывает как синхронные, и асинхронные ошибки.

Рассмотреть возможность

function throws() {
  throw Error('synchronous failure');
}


async function rejects() {
  throw Error('asynchronous failure');
}

(async function main() {
  try {
    throws();
  } catch (e) {
    console.log('asynchronously handled', e.message);

  }

  try {
    await rejects();
  } catch (e) {
    console.log('asynchronously handled', e.message);
  }
}());

Важно помнить, что асинхронные ошибки не будут обнаружены, если вы забудете await промис, который отклоняется вместе с ними. Это аналог забывания return внутри .then или обратного вызова .catch.

person Aluan Haddad    schedule 08.02.2018

Вы можете просто выбросить и исключить, если ваше значение окажется пустым.

export const getById = async (id) => {
        const user = await users.findOne({ where: {
                id: id
            }});

        if (!user) {
            throw Error('User does not exist..');
        }
        return user;        
}
person Espen    schedule 08.02.2018
comment
Это не сработает, так как вы находитесь в середине попытки/поймать, и выброшенная ошибка будет перехвачена, а затем возвращена из асинхронной функции, а обещания не должны работать. Просто полностью удалите обертку try/catch, и будет намного лучше. - person greim; 08.02.2018