【问题标题】:Why aren’t these Bluebird promise chains equivalent?为什么这些 Bluebird 承诺链不等价?
【发布时间】:2015-02-13 03:52:42
【问题描述】:

我在我的 Javascript 代码中引入了使用 Bluebird 库的 Promise,因为它们似乎是最接近我在 Haskell 中使用的流控制组合器的可用东西。以下 Gulp 任务对我有用:

gulp.task('upload', function(callback) {
  configAWSFromFile();
  var functionName = 'redacted',
      Lambda = new AWS.Lambda();
  Promise.promisifyAll(Object.getPrototypeOf(Lambda));

  function uploadLambdaFn(lambdaFn, fileStream) {
    var params = {
      FunctionName: functionName,
      Handler: lambdaFn.Configuration.Handler,
      // snip
      FunctionZip: fileStream
    };
    return Lambda.uploadFunctionAsync(params);
  }

  return Promise.join(Lambda.getFunctionAsync({FunctionName: functionName}),
                      fs.readFileAsync(path.join(destPrefix, uploadPackage)),
                      uploadLambdaFn)
    .then(function(response) {
      gutil.log('Response:\n' + util.inspect(response));
    });
});

但是,当我尝试从配置对象创建功能中重构上传步骤时,它会崩溃:

gulp.task('upload', function(callback) {
  configAWSFromFile();
  var functionName = 'redacted',
      Lambda = new AWS.Lambda();
  Promise.promisifyAll(Object.getPrototypeOf(Lambda));

  // rename function
  function mkLambdaFnConfig(lambdaFn, fileStream) {
    var params = {
      FunctionName: functionName,
      Handler: lambdaFn.Configuration.Handler,
      // snip
      FunctionZip: fileStream
    };
    return params;  // return immediate value
  }

  return Promise.join(Lambda.getFunctionAsync({FunctionName: functionName}),
                      fs.readFileAsync(path.join(destPrefix, uploadPackage)),
                      Promise.method(mkLambdaFnConfig))  // wrap regular function
    .then(Lambda.uploadFunctionAsync)  // add the upload to the chain
    .then(function(response) {
      gutil.log('Response:\n' + util.inspect(response));
    });
});

提供以下堆栈跟踪(路径已被编辑为相对):

TypeError: undefined is not a function
    at svc.(anonymous function) (./node_modules/aws-sdk/lib/service.js:399:21)
    at tryCatcher (./node_modules/bluebird/js/main/util.js:24:31)
    at ret (eval at <anonymous> (./node_modules/bluebird/js/main/promisify.js:154:12), <anonymous>:13:39)
    at tryCatcher (./node_modules/bluebird/js/main/util.js:24:31)
    at Promise._settlePromiseFromHandler (./node_modules/bluebird/js/main/promise.js:466:31)
    at Promise._settlePromiseAt (./node_modules/bluebird/js/main/promise.js:545:18)
    at Promise._settlePromises (./node_modules/bluebird/js/main/promise.js:661:14)
    at Async._drainQueue (./node_modules/bluebird/js/main/async.js:79:16)
    at Async._drainQueues (./node_modules/bluebird/js/main/async.js:89:10)
    at Immediate.Async.drainQueues [as _onImmediate] (./node_modules/bluebird/js/main/async.js:14:14)
    at processImmediate [as _immediateCallback] (timers.js:358:17)

如果我将mkLambdaFnConfig 函数更改为返回Promise.resolve(params) 而不是将mkLambdaFnConfig 包装在Promise.method 中,我会得到相同的堆栈跟踪:

gulp.task('upload', function(callback) {
  configAWSFromFile();
  var functionName = 'redacted',
      Lambda = new AWS.Lambda();
  Promise.promisifyAll(Object.getPrototypeOf(Lambda));

  // rename function
  function mkLambdaFnConfig(lambdaFn, fileStream) {
    var params = {
      FunctionName: functionName,
      Handler: lambdaFn.Configuration.Handler,
      // snip
      FunctionZip: fileStream
    };
    return Promise.resolve(params);  // return already-fulfilled promise
  }

  return Promise.join(Lambda.getFunctionAsync({FunctionName: functionName}),
                      fs.readFileAsync(path.join(destPrefix, uploadPackage)),
                      mkLambdaFnConfig)  // no additional wrapping
    .then(Lambda.uploadFunctionAsync)  // add the upload to the chain
    .then(function(response) {
      gutil.log('Response:\n' + util.inspect(response));
    });
});

生成堆栈跟踪(同样,使用相对路径):

TypeError: undefined is not a function
    at svc.(anonymous function) (./node_modules/aws-sdk/lib/service.js:399:21)
    at tryCatcher (./node_modules/bluebird/js/main/util.js:24:31)
    at ret (eval at <anonymous> (./node_modules/bluebird/js/main/promisify.js:154:12), <anonymous>:13:39)
    at tryCatcher (./node_modules/bluebird/js/main/util.js:24:31)
    at Promise._settlePromiseFromHandler (./node_modules/bluebird/js/main/promise.js:466:31)
    at Promise._settlePromiseAt (./node_modules/bluebird/js/main/promise.js:545:18)
    at Promise._settlePromises (./node_modules/bluebird/js/main/promise.js:661:14)
    at Async._drainQueue (./node_modules/bluebird/js/main/async.js:79:16)
    at Async._drainQueues (./node_modules/bluebird/js/main/async.js:89:10)
    at Immediate.Async.drainQueues [as _onImmediate] (./node_modules/bluebird/js/main/async.js:14:14)
    at processImmediate [as _immediateCallback] (timers.js:358:17)

我怀疑我遗漏了 Promise 工作原理的一些基本组成部分。我原以为 Promise.resolvePromise.method 是提升像 monadic return 和 applicative pure 这样的函数,但我现在猜这是一个严重的误解。

为未来的访问者编辑:

这是我最终得到的工作代码(在@Bergi 指出这只是一个绑定问题之后):

gulp.task('upload', function(callback) {
  configAWSFromFile();
  var functionName = 'redacted',
      Lambda = new AWS.Lambda();
  // As noted by @Esailija, this should be moved to init.
  Promise.promisifyAll(Object.getPrototypeOf(Lambda));

  function mkLambdaFnConfig(lambdaFn, fileStream) {
    var params = {
      FunctionName: functionName,
      Handler: lambdaFn.Configuration.Handler,
      // snip
      FunctionZip: fileStream
    };
    return params;
  }

  return Promise.join(Lambda.getFunctionAsync({FunctionName: functionName}),
                      fs.readFileAsync(path.join(destPrefix, uploadPackage)),
                      Promise.method(mkLambdaFnConfig))  // wrap synchronous function
    // Methods added as callbacks need to be bound, just like usual!
    .then(Lambda.uploadFunctionAsync.bind(Lambda))
    .then(function(response) {
      gutil.log('Response:\n' + util.inspect(response));
    });
});

【问题讨论】:

  • 我认为问题不在于您对承诺的理解。 (尽管您可能想注意到 JavaScript 在类型方面非常松懈,但 Promise 不能嵌套,并且它不关心您是返回值还是 Promise - 您不能真正忘记像 Haskell 中的 return )。
  • 出于好奇,您在 Haskell 中使用了哪些异步组合器?
  • 请将运行时代码中的Promise.promisifyAll(Object.getPrototypeOf(Lambda)); 移到初始化代码中,您真的不想在应用程序初始化后调用该函数:D
  • 谢谢@Bergi,我一直在努力理解承诺中涉及的元编程,却错过了这是一个简单的绑定问题。
  • @Bergi:对于任何给定应用程序的主要抽象,我经常发现根据无限流设计事物很有用。然后我可以使用 Michael Snoyman 的优秀 conduits。但大多数时候在 Haskell 中,我使用的任何库都带有相同的旧类型类提供的组合器:Functor、Applicative、Monad、Arrow。

标签: javascript node.js promise bluebird


【解决方案1】:

这里的问题是你改变了返回值的类型;虽然您的 uploadLambdaFn 函数返回一个 Bluebird 承诺,但您的 MkLambdaFn 不是。在静态类型、严格类型的程序中,编译器会为您捕捉到这一点。使用 Javascript,除非您知道要查找什么,否则可能会很麻烦。

您的程序正在中断,因为某些东西试图调用您的 Promise 对象上的方法 - 但由于它不是一个 Promise,它没有该方法,因此会出错。

您可以通过将返回值包装在 promise 中来解决此问题。我不记得 Bluebird 是如何实现这一点的(而且我无法从我的工作中访问 Github),但我稍后会编辑答案以提供一些代码。 Promise.resolve() 不适合你,但应该有一些应该的东西。

【讨论】:

    猜你喜欢
    • 2016-06-30
    • 1970-01-01
    • 2019-04-29
    • 2022-01-15
    • 2019-01-25
    • 2017-04-02
    • 2018-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多