【问题标题】:Catching and returning an error from a callback that may be asynchronous从可能是异步的回调中捕获并返回错误
【发布时间】:2021-03-28 14:05:25
【问题描述】:

我有这样的功能:

const x = y(callback);
x();

可以通过同步或异步回调调用:

const a = y(() => 42);
const b = y(async () => Promise.resolve(42));

函数y 应该接受一个回调,它可以是同步的或异步的。是否可以从同步或异步回调中捕获抛出的错误?

我不能简单地将回调包装在 try/catch 中:

const y = (callback) => function () {
  try {
    return callback(...arguments);
  }
  catch (callbackError) {
    throw callbackError;
  }
};

因为它没有捕捉到 Promise 抛出的错误。我不能只将.catch 链接到回调,因为它可能不是一个承诺。

我读过在调用函数may not trivial/may not be possible之前检查函数是否是一个承诺。

在将catch 链接到它之前,我不能调用它来检查它是否返回一个承诺,因为如果它抛出,catch 将不会被链接到调用上

有没有办法从可能是也可能不是承诺的回调中捕获错误?


根据 cmets,我应该指定我不希望函数返回承诺。那就是:

const a = y(() => { throw new Error(); })();
console.log(a instanceof Error);

const b = y(async () => { throw new Error(); })();
console.log(b instanceof Error);

const c = y(() => 42)();
console.log(c === 42);

const d = y(async () => Promise.resolve(42))();
console.log(d instanceof Promise);

应该都是真的。简单来说就是想返回错误或者结果

【问题讨论】:

  • 第一个建议。也许把它们都当作承诺。在顶部的 Promise.resolve(blackBoxFunction).catch() 中包装这个“正常”回调,然后从中捕获错误
  • @Robert,我应该在问题中指定,我不希望函数在抛出错误时返回承诺
  • 好的。但我认为您应该考虑以相同方式处理异步而不是异步回调的代码的统一行为。我不确定检查并设置条件何时是异步是个好主意。 (也许你的情况是,但总的来说我很确定不是)。微任务队列的延迟通常是问题。
  • @Robert 此函数将在将错误作为 promise 库的一部分返回之前对其进行修改,因此不幸的是,在这种情况下它可能是必要的。我会继续考虑是否是这种情况 - 谢谢!

标签: javascript asynchronous async-await promise


【解决方案1】:

一个异步函数也会发出一个promise,你可以考虑将异步包装在一个promise中,它在解析时会发出值

【讨论】:

  • 不幸的是,我已经编辑了这个问题,以澄清这应该总是返回一个承诺的误解
【解决方案2】:

意识到没有必要立即链接catch!所以下面的可憎让我确定它是否是一个承诺并相应地处理错误:

const y = (error, callback) => function () {
  try {
    const returnValue = callback(...arguments);

    if (returnValue instanceof Promise) {
      return returnValue.catch((callbackError) => {
        return callbackError;
      });
    }
    else {
      return returnValue;
    }
  }
  catch (callbackError) {
    return callbackError;
  }
};

如果您想知道为什么这个函数很有用,这里有一个我使用它的例子。它会在返回之前修改错误(将其与另一个错误链接,请参阅VError):

const y = (error, callback) => function () {
  try {
    const returnValue = callback(...arguments);

    if (returnValue instanceof Promise) {
      return returnValue.catch((callbackError) => {
        throw VError(error, callbackError);
      });
    }
    else {
      return returnValue;
    }
  }
  catch (callbackError) {
    throw VError(error, callbackError);
  }
};

请注意根据问题返回以下内容:

const a = y(() => 42)(); // returns '42'
const b = y(async () => Promise.resolve(42))(); // returns 'Promise {42}'
const c = y(() => { throw new Error('Base'); })(); // throws VError
const d = y(async () => { throw new Error('Base'); })(); // throws VError

【讨论】:

    【解决方案3】:

    您可以在此处使用async/await 表示法。 Await 将与同步和异步函数一起使用。

    尝试做:

    async function init() {
      const x = y( async () => await callback() );
      try {
        await x();
      }
      catch(error) {
        /.../
      }
    }
    

    您的功能现在可以简化为:

    
    const y = (callback) => function (...argument) {
        return callback(...arguments); 
    };
    

    【讨论】:

    • 请注意,这将始终返回一个承诺 - 函数是 async。特别不希望每个返回值都是一个承诺
    【解决方案4】:

    好吧,您可以通过使所有代码异步来做类似的事情...如果我解决了您的问题。

    UPD:了解问题,这是我的方法

    (async () => {
    
      // Run fn
      const fn = callback => {
        return new Promise(async (resolve, reject) => {
            const res = await callback().catch(err => reject(err));
            // Resolve
            resolve(res);
        });
      };
    
      const variousFNs = [
        // Function 1
        {
          fn: () => 2+2
        },
        // Function 2 with error
        {
          fn: () => {
            const x = 1;
            x = 2;
            return x;
          }
        },
        // Promise Function 3
        {
          fn: () => Promise.resolve(8)
        },
        // Promise function 4 with error
        {
          fn: () => new Promise(async (resolve, reject) => reject('some promise error'))
        }
      ];
      
      //
      //
      // Run all your fns
      for (let i = 0; i < variousFNs.length; i++) {
        try {
          const res = await variousFNs[i].fn();
          console.log(res);
        } catch(err) {
          console.log(`Error catched: ${err}`);
        }
      }
    
    })();

    【讨论】:

    • fn 总是返回一个承诺,而不是问题中指定的 cd 的错误
    • 好的,我了解您的问题,请检查我的 UPD
    • 你的函数 fn 仍然总是返回一个 Promise。 fn 是您在这里提供的答案吗?
    • 嗯,是的,fn 返回 promise,但是你可以将普通函数或异步函数传递给它,并且总是得到结果或错误。如果你不等待 promise,你将返回 Promise 函数而不是结果。
    猜你喜欢
    • 1970-01-01
    • 2014-12-01
    • 2011-04-10
    • 2018-11-30
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    相关资源
    最近更新 更多