【问题标题】:Convert callback to Promise将回调转换为 Promise
【发布时间】:2018-04-23 22:08:58
【问题描述】:

我想将下面的 callEndPointWrapper 转换为返回 Promise 而不是回调。 我已经使用下面的代码进行了测试,但是等待 Promise for callEndpointWrapper() 请求超时。 我错过了下面的任何东西吗? (在调试时,我在等待 Promise 时看到下面的代码行中的请求超时: return (function callEndpoint(callback): any {

回调:

    function callEndPointWrapper(): any {
        return function callEndpoint(callback) {
            try {
                // gRPC call
                client[methodName](req, options, callback);
            }
            catch (err) {
                callback(err);
            }
        };
    }
const result = yield callEndpointWrapper();
// I get the correct result from callback above (both on success or error)

承诺:

function callEndPointWrapper(): Promise<any> {
  return new Promise( (resolve, reject) => {
    return (function callEndpoint(callback): any {
      try {
        // gRPC call
        resolve(client[methodName](req, options, callback));
      } catch (err) {
        reject(callback(err));
      }
    });
  });
const result = await callEndpointWrapper();
// Above request times out.

【问题讨论】:

  • 这段代码的作用有点令人困惑。基于回调的代码返回一个接受回调的函数,但返回的函数实际上是做一些异步工作的函数......你的意思是你需要像await callEndpointWrapper()()......(注意2调用)?如果您没有在 callEndpoint 函数的闭包中传递任何可用的配置选项,那么拥有包装函数有什么意义?
  • @nem035 感谢您的回复,它应该是一个单独的调用,即await callEndPointWrapper(),并且在调用callEndpointWrapper 之前,请求(req)和选项被设置为全局变量。跨度>
  • 你在使用co吗?
  • 是的,我之前使用的包装器co

标签: javascript node.js callback promise


【解决方案1】:

callEndPointWrapper 的预期结果似乎是一个函数 (callEndPoint),它执行一些异步工作,您可以进一步调用它来做某事。

在您的回调方法中,您正在生成此 callEndPoint 函数。

----> callEndPointWrapper 返回 callEndPoint 执行异步工作。

另一方面,在基于承诺的方法中,您试图生成 callEndPoint 的结果,而不是返回 callEndPoint 本身。事实上,callEndPoint 从未在 Promise 构造函数中调用。

----> callEndPointWrapper 返回一个永远不会解析的承诺,并在内部创建 callEndPoint 函数,该函数什么都不做,因为它从未被调用过。

请务必注意,对 callEndPointWrapper 的单个调用不是异步的。实际的异步部分(假设 client 方法是异步的)发生在 callEndpoint 中,因此基于回调的方法的异步调用将类似于:

callEndPointWrapper()(
  function callback(responseFromEndpoint) {
    // ...
  }
)

// or something like
let caller = callEndPointWrapper();
caller(function callback(responseFromEndpoint) {
    // ...
});

由此可知,基于 Promise 的方法也需要两次调用:

await callEndPointWrapper()(); // call a wrapper which returns a function which returns a promise

回调代码的功能等效(就产生的结果而言)基于承诺的代码如下:

function callEndPointWrapper(): any {
    return function callEndpoint() {
      return new Promise((resolve, reject) => {
        try {
          client[methodName](req, options, (err, result) => {
            if (err) return reject(err);
            resolve(result);
          });
        } catch (err) {
          // Note: this rejection will only happen on any sync errors
          // with the above code, such as trying to invoke a non-existing
          // method on the client. This type of error is a programmer error
          // rather than an operational error in the system so you should
          // consider if such errors should even by caught by your code.
          reject(err);
        }
      });
   };
}

但是,这导致了一个问题,如果您没有传递任何配置选项以在 callEndpoint 函数的闭包中可用,那么拥有包装函数的意义何在?

根据您的示例用法,您只需要callEndpoint 方法。


想到您可能正在使用类似co 的东西,它允许您使用yield 函数(thunk)并通过回调在内部调用它们。

所以当你这样做时

yield callEndpointWrapper()

你实际上是在打电话

yield function callEndpoint(callback) {
  // ...
}

然后co 会在幕后为您施展魔法。

这是deprecated by co 的模式,总体上不建议使用。更不用说需要了解特定库的非常令人困惑(和丑陋的恕我直言)的行为。

要对co 使用promise,您不需要包装函数。只是yieldawait 调用callEndPoint 的结果(与上面我的promise 示例中的相同),这将是一个promise。

yield callEndPoint()
// or
await callEndPoint()

【讨论】:

    猜你喜欢
    • 2017-03-22
    • 2015-12-11
    • 1970-01-01
    • 2016-08-11
    • 2018-09-19
    • 1970-01-01
    • 2020-10-19
    • 2021-01-25
    相关资源
    最近更新 更多