Обработка ошибок с использованием try / catch внутри async / await в облачной функции firebase onCall - PullRequest
0 голосов
/ 12 марта 2019

В настоящее время я развертываю эту облачную функцию в своем приложении Firebase и буду использовать среду выполнения Node v8, поэтому я могу использовать синтаксис async / await .

У меня возникли проблемы с обработкой различных типов ошибок, которые могут возникать в одной и той же функции.

После завершения функция должна получить параметр url для отправки запроса.к этому url , почистите тело response для некоторых данных и сохраните его в базе данных.На данный момент, он просто возвращает ту же строку URL, которую он получил для целей тестирования.

Пока у меня есть следующее:

const functions = require('firebase-functions');
const request = require('request');
const cheerio = require('cheerio');

exports.getDataFromUrl = functions.https.onCall((data) => {

  // PROMISIFIED REQUEST TO USE WITH ASYNC AWAIT
  const promisifiedRequest = function(options) {
    return new Promise((resolve,reject) => {
      request(options, (error, response, body) => {
        if (error) {
          return reject(error);
        }
        return resolve(response);
      });
    });
  };

  // CHECK IF URL IS PRESENT, IF NOT, THROW ERROR
  if (!data.url) {
    throw new functions.https.HttpsError('invalid-argument','The URL parameter was invalid.');
  }

  // URL passed from the client.
  const url = data.url;

    // IIFE ASYNC FUNCTION
    (async function() {

      // TRY BLOCK
      try {

        // REQUEST OPTIONS TO THE URL
        const urlOptions = {
          url: 'https://www.someINEXISTENT.url',
          method: 'GET',
          gzip: true,
          headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36'
          },
          jar: true
        };

        // CREATE RESPONSE AND CHEERIO OBJECT
        let response = null;
        let $ = null;

        // SEND REQUEST TO URL, AND PARSE WITH CHEERIO
        response = await promisifiedRequest(urlOptions);
        $ = cheerio.load(response.body);

      } // TRY BLOCK - END

      // CATCH BLOCK
      catch (error) {
        console.log('Caught an error: ' + error);
        throw new functions.https.HttpsError('unknown', error.message, error);
      }

      console.log('End of async function...');

    })()

  return {
    yourUrl : url
  };

});

Мой первый случай ошибки, который происходиткогда URL недействителен, работает просто отлично.Выполнение останавливается, когда я выкидываю следующую ошибку:

throw new functions.https.HttpsError('invalid-argument','URL invalid.');

Насколько я понял, нужно выкинуть это HttpsError, чтобы можно было поймать клиента,И это работает.Я получаю эту ошибку на клиенте, когда это происходит.

enter image description here

Моя проблема связана со вторым типом ошибки, которая должна быть обнаружена оператор try / catch внутри моей асинхронной функции .Эта ошибка должна возникать, когда я пытаюсь запросить несуществующий URL, например.

Что происходит, вот что (рисунок ниже):

Блок catch активирован, и я вижу `console.log () на моей консоли функций, но почему-то не выдает ошибку, и он жалуется на то, что «выбрасывает внутри асинхронную функцию без блока catch», хотя я выкидываю его из блока catch.Мой клиентский код не получает эту ошибку вообще.С точки зрения моего клиента, функция завершается без каких-либо ошибок.

enter image description here

Ошибка:

Ошибка: (узел: 5160) UnhandledPromiseRejectionWarning: необработанное отклонение обещания.Эта ошибка возникла либо в результате выброса асинхронной функции без блока catch, либо в результате отклонения обещания, которое не было обработано с помощью .catch ().(код отклонения: 1)

Я попытался добавить и внешние try/catch блоки.А также использование .catch() после асинхронного IIFE для выброса ошибки за пределы асинхронной функции, но это не решило проблему.

Что я делаю не так?

1 Ответ

1 голос
/ 12 марта 2019

... но почему-то это не выдает ошибку ...

Это потому, что ничто не использует обещание, которое создает функция async, которое отклоняется throw.Одно из правил обещаний таково: вы должны обработать отклонение обещания или вернуть результат чему-то другому, что будет.

functions.https.onCall позволяет вам вернуть обещание , поэтому я бы сделалобратный вызов функции async вместо использования асинхронного IIFE.Вы не можете синхронно предоставить свой результат или выдать ошибку, потому что вы имеете дело с асинхронной операцией.

Что-то в этом духе (может потребоваться настройка, см. *** комментарии):

const functions = require('firebase-functions');
const request = require('request');
const cheerio = require('cheerio');

// *** No reason to define this within `onCall`'s handler
const promisifiedRequest = function(options) {
  return new Promise((resolve,reject) => {
    request(options, (error, response, body) => {
      if (error) {
        return reject(error);
      }
      return resolve(response);
    });
  });
};

exports.getDataFromUrl = functions.https.onCall(async (data) => { // *** Make it async, since `onCall` allows you to return a promise

  // CHECK IF URL IS PRESENT, IF NOT, THROW ERROR
  if (!data.url) {
    throw new functions.https.HttpsError('invalid-argument','The URL parameter was invalid.');
  }

  // URL passed from the client.
  const url = data.url;

  // *** No need for try/catch unless you want to change the error
  // *** If you do, though, add it back and use `throw` in the `catch` block (as you did originally).
  // *** That will make the promise this async function returns reject.

  // REQUEST OPTIONS TO THE URL
  const urlOptions = {
    url: 'https://www.someINEXISTENT.url',
    method: 'GET',
    gzip: true,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.96 Safari/537.36'
    },
    jar: true
  };

  // CREATE RESPONSE AND CHEERIO OBJECT
  let response = null;
  let $ = null;

  // SEND REQUEST TO URL, AND PARSE WITH CHEERIO
  response = await promisifiedRequest(urlOptions);
  $ = cheerio.load(response.body);

  // *** Do you really want to return the URL? Not something from the body of what you requested?
  return {
    yourUrl : url
  };
});
...