Javascript 在firebase onCall云函数上使用异步/等待内部的try/catch进行错误处理

Javascript 在firebase onCall云函数上使用异步/等待内部的try/catch进行错误处理,javascript,firebase,async-await,try-catch,google-cloud-functions,Javascript,Firebase,Async Await,Try Catch,Google Cloud Functions,我目前正在将此云功能部署到我的firebase应用程序中,并且我将使用Node v8运行时,因此我可以使用async/await语法 我在处理同一函数中可能出现的各种错误时遇到了一些问题 完成后,函数应接收一个url参数,以请求该url,从响应正文中获取一些数据,并将其保存到数据库中。此时,它只是返回它为测试目的而收到的相同url字符串 到目前为止,我的情况如下: const functions = require('firebase-functions'); const request = r

我目前正在将此云功能部署到我的firebase应用程序中,并且我将使用Node v8运行时,因此我可以使用async/await语法

我在处理同一函数中可能出现的各种错误时遇到了一些问题

完成后,函数应接收一个url参数,以请求该url,从响应正文中获取一些数据,并将其保存到数据库中。此时,它只是返回它为测试目的而收到的相同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无效时发生)运行正常。当我抛出以下错误时,执行停止:

抛出新函数.https.HttpsError('invalid-argument','URL invalid')

据我所知,需要抛出这个
HttpsError
,以便能够了解客户机。它是有效的。我在客户端上得到这个错误,当它发生时

我的问题是第二类错误,应该由我的异步函数中的try/catch语句捕获。例如,当我试图请求一个不存在的url时,就会发生这个错误

发生的情况如下(下图):

catch块被激活了,我可以在我的函数控制台上看到`console.log(),但不知怎么的,它没有抛出错误,它抱怨“在没有catch块的异步函数中抛出”,即使我是从catch块中抛出它。我的客户端代码根本没有收到这个错误。从我的客户的角度来看,函数完成时没有任何错误

错误:

错误:(节点:5160)未处理的PromisejectionWarning:未处理的承诺 拒绝。此错误源于在异步 F未设置挡块的行为,或拒绝不符合要求的承诺 用.catch()处理。(拒绝id:1)

我已经尝试添加和外部
try/catch
块。还可以在异步IIFE之后使用
.catch()
将错误抛出异步函数之外,但这并没有解决问题

我做错了什么

…但不知怎的,它没有抛出错误

这是因为没有任何东西使用
async
函数创建的承诺,该承诺被
throw
拒绝。承诺的规则之一是:你必须处理承诺被拒绝的情况,或者将结果返回给其他可能的人

functions.https.onCall
允许您这样做,因此我将使回调成为一个
async
函数,而不是使用异步IIFE。您无法同步提供结果或抛出错误,因为您正在处理异步操作

以下内容(可能需要调整,请参见
***
注释):


如果要显示错误消息的文本,请将其复制到问题本身,而不是添加屏幕截图。截图很难阅读,也不可能在上面搜索。谢谢。我将编辑并添加它。这很有效!我刚刚添加了一个
try
块,其中包含
wait
调用。然后我在返回之前从一个
catch
块抛出
错误。我认为这是必要的,因为我需要抛出那个错误来停止函数,并在那个时候将错误发送给客户端。最后返回的url仅用于测试目的。非常感谢。@cbdev420-太好了!很高兴这有帮助,我还没有做过Firebase,但医生说你可以回报一个承诺,所以。。。如果要停止函数并在此时将错误发送给客户端,则不需要捕获并重新显示错误。如果要抛出与已抛出的错误不同的错误,只需捕获并重新抛出它。快乐编码!你是对的!即使没有
catch
块,Firebase也会向我的客户端抛出(错误代码=内部)。尽管在我的函数日志中,它抱怨发生了
未处理的错误
。所以我最好还是处理好它。由于我们在讨论这个问题,我一直在考虑以“总是成功”的方式处理函数中的错误,即:捕获错误但不抛出异常,我将成功返回错误对象,而不是常规响应,并在客户端检查。客户端总是进入
。然后()
。您认为这两种方法都有(dis)优点吗?我会仔细检查结果,如果Firebase将错误返回到您的应用程序,那么它不是未经处理的,并且不应该存在未经处理的拒绝错误。你总是能成功:我不喜欢这样做,在我看来,成功和失败的道路是分开的。我会听从你的建议。非常感谢你的帮助!
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
  };
});