UnhandledPromiseRejectionWarning:回调已被调用(环回远程方法)

UnhandledPromiseRejectionWarning: Callback was already called (Loopback remote method)

我收到一个错误

(node:27301) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Callback was already called.

根据我对 await 和 Mozilla 描述中拒绝承诺的理解:

If the Promise is rejected, the await expression throws the rejected value.

我拒绝回调中包含我的 Promise 的错误,如下所示:

Airport.nearbyAirports = async (location, cb) => {
  let airports
  try {
    airports = await new Promise((resolve, reject) => {
      Airport.find({
        // code
      }, (err, results) => {
        if (err)
          reject(err) // Reject here
        else
          resolve(results)
      })
    })
  } catch (err) { // Catch here
    cb(err, null)
    return
  }
  if (!airports.empty)
    cb(null, airports)
  }

我的问题是

  1. 为什么它仍然认为我的承诺拒绝未处理?我认为 catch 语句应该消除这个错误。
  2. 为什么它认为我的回调已经调用了?我的 catch 中有一个 return 语句,因此永远不应调用两者。

如果 Airport.find() 抛出异常,那么执行将跳转到您的 catch 块并且您的 Promise 将永远不会被解决或拒绝。也许您需要将其包装在自己的 try/catch:

Airport.nearbyAirports = async (location, cb) => {
  let airports
  try {
    airports = await new Promise((resolve, reject) => {
      try {
        Airport.find({
          // code
        }, (err, results) => {
          if (err)
            reject(err) // Reject here
          else
            resolve(results)
        })
      } catch (err) {
        reject(err) // Reject here too
        cb(err, null)
      }
    })
  } catch (err) { // Catch here
    cb(err, null)
    return
  }
  if (!airports.empty)
    cb(null, airports)
  }

问题实际上是我的框架(LoopbackJS),而不是我的函数。显然在撰写本文时,不支持使用承诺:

https://loopback.io/doc/en/lb3/Using-promises.html#setup

意味着我什至无法在我的函数中使用 await,因为远程方法将我的函数包装在其他地方,所以 async 将始终未处理。我最终回到了内部代码的基于 Promise 的实现:

Airport.nearbyAirports = (location, cb) => {
const settings = Airport.dataSource.settings
const db = DB(settings)
let airports
NAME_OF_QUERY().then((res) => {
  cb(null, res)
}).catch((err) => {
  cb(err, null)
})

said here 一样,loopback 3 通过允许您使用简单的 return.

来支持这一点

这个:

Entry.findFooById = async (id, cb) => {
  const result = await Entry.findById(id);
  return result;
};

...等同于:

Entry.findFooById = (id, cb) => {
  Entry.findById(id)
    .then(result => cb(null, result))
    .catch(cb);
};

我们使用 Loopback 2.31.0,它还支持用于远程方法的异步函数的简单 return。如果您在远程方法的某处放置一个断点并在调用堆栈中跳到它上面的一级,您将看到它是如何在环回本身中实现的 (shared-method.js):

  // invoke
  try {
    var retval = method.apply(scope, formattedArgs);
    if (retval && typeof retval.then === 'function') {
      return retval.then(
        function(args) {
          if (returns.length === 1) args = [args];
          var result = SharedMethod.toResult(returns, args);
          debug('- %s - promise result %j', sharedMethod.name, result);
          cb(null, result);
        },
        cb // error handler
      );
    }
    return retval;
  } catch (err) {
    debug('error caught during the invocation of %s', this.name);
    return cb(err);
  }
};

它在这里做什么 - 它调用你的函数,如果它是一个异步函数 - 它会 return 一个承诺(retval.then === 'function' 将是 true)。在这种情况下,环回将正确处理您的结果,作为承诺。它还会为您进行错误检查,因此您的代码中不再 try/catch 块。

因此,在您自己的代码中,您只需要像下面这样使用它:

Airport.nearbyAirports = async (location) => {
    let airports = await new Promise((resolve, reject) => {
        Airport.find({
            // code
        }, (err, results) => {
            if (err)
                reject(err) // Reject here
            else
                resolve(results)
        })
    });

    if (!airports.empty)
        return airports;
    }
    else  {
        return {}; // not sure what you would like to return here as it wan not handled in your sample...
    }
}

注意,这里根本不需要使用回调(cb)。