简体   繁体   English

这些Bluebird Promise链为什么不等效?

[英]Why aren’t these Bluebird promise chains equivalent?

I'm introducing promises using the Bluebird library to my Javascript code, as they seem to be the closest available thing to the flow control combinators I'm used to in Haskell. 我正在使用Bluebird库向我的Javascript代码引入promises,因为它们似乎是我在Haskell中习惯使用的流控制组合器最接近的东西。 The following Gulp task works for me: 以下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));
    });
});

However, when I try to refactor the upload step out of the config object creation function, it breaks down: 但是,当我尝试通过配置对象创建功能重构上载步骤时,它崩溃了:

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));
    });
});

Gives the following stack trace (paths have been edited to be relative): 提供以下堆栈跟踪(路径已被编辑为相对路径):

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)

I get an identical stack trace if I change the mkLambdaFnConfig function to return Promise.resolve(params) instead of wrapping mkLambdaFnConfig in Promise.method : 如果我更改mkLambdaFnConfig函数以返回Promise.resolve(params)而不是在Promise.method中包装mkLambdaFnConfig ,我会获得相同的堆栈跟踪:

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));
    });
});

Generates the stack trace (again, with relative paths): 生成堆栈跟踪(同样,使用相对路径):

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)

I suspect that I'm missing some basic component of how promises work. 我怀疑我错过了承诺如何运作的一些基本组成部分。 I had thought that Promise.resolve and Promise.method were lifting functions like monadic return and applicative pure , but I'm guessing now that's a gross misunderstanding. 我以为Promise.resolvePromise.method提升了诸如Promise.method return和applicative pure ,但是我猜现在这是一个严重的误解。

Edit for future visitors: 为将来的访问者编辑:

This is the working code I ended up with (after @Bergi pointed out that it's just a binding issue): 这是我最终得到的工作代码(在@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));
    });
});

The problem here is that you've changed the type of your return value; 这里的问题是您更改了返回值的类型。 while your uploadLambdaFn function returns a Bluebird promise, your MkLambdaFn is not. 当您的uploadLambdaFn函数返回Bluebird承诺时,您的MkLambdaFn不会。 In a statically-typed, strictly-typed program, the compiler would catch this for you. 在一个静态类型的,严格类型的程序中,编译器会为您捕获这个。 With Javascript, unless you know what to look for, it can be a tripup. 使用Javascript,除非你知道要寻找什么,否则它可能是一个旅行。

Your program is breaking because something's trying to call a method on your Promise object - but since it's not a promise, it doesn't have that method, and thus errors. 你的程序正在破坏,因为有些东西试图在你的Promise对象上调用一个方法 - 但由于它不是一个promise,它没有那个方法,因而也就是错误。

You can fix this by wrapping your return value in a promise. 您可以通过在promise中包装返回值来解决此问题。 I don't remember how Bluebird implements this (and I can't access Github from my work), but I'll edit the answer later to provide some code. 我不记得Bluebird如何实现这一点(我无法从我的工作中访问Github),但我稍后会编辑答案以提供一些代码。 Promise.resolve() isn't working for you, but there should be something that should. Promise.resolve()不适合你,但应该有一些东西应该。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM