为什么这些Bluebird承诺链不等同?

时间:2015-02-11 22:34:43

标签: javascript node.js promise bluebird

我正在使用Bluebird库向我的Javascript代码引入promises,因为它们似乎是我在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.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));
    });
});

1 个答案:

答案 0 :(得分:0)

这里的问题是你改变了返回值的类型;当您的uploadLambdaFn函数返回Bluebird承诺时,您的MkLambdaFn不会。在一个静态类型的,严格类型的程序中,编译器会为您捕获这个。使用Javascript,除非你知道要寻找什么,否则它可能是一次旅行。

你的程序正在破坏,因为某些东西试图在你的Promise对象上调用一个方法 - 但由于它不是一个promise,它没有那个方法,因而也就是错误。

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