【问题标题】:node.js promise loop architecture with bluebirdnode.js 承诺循环架构与蓝鸟
【发布时间】:2015-11-20 15:38:42
【问题描述】:

我最近开始学习使用 node.js 编写应用程序,但一切的异步特性给我带来了一些麻烦。

我正在尝试编写一个遍历对象数组的脚本(下面的示例)。每个对象都包含几个指向我要下载的文件(plist、png)的 url。下载后,我想从中获取一些数据,然后根据该数据裁剪另一个。

当我创建一个对象并将其传递给承诺链(这是我在下面提供的示例)时,我有这个工作。我遇到的问题是当我尝试使用Promise.eachPromise.allPromise.mapSeries 时。当我使用其中任何一个时,很明显(按照我的 console.log 语句的顺序)它们都立即运行,而不是一次运行。

这是我工作的一个例子。抱歉,篇幅太长,我尽量保持整洁,以便于理解。

// ---------------------------------------------------------------------------

var Promise = require("bluebird"),
  fs = Promise.promisifyAll(require("fs")),
  gm = Promise.promisifyAll(require("gm")),
  plist = require("plist"),
  request = require("request-promise")


// ---------------------------------------------------------------------------
// Test Data
// This is what I'd like to replace with an array which would contain a few hundred of these

var card = {
  slug: "neutral_zurael",
  plist: "https://assets-counterplaygames.netdna-ssl.com/production/resources/units/neutral_zurael.plist",
  sprite: "https://assets-counterplaygames.netdna-ssl.com/production/resources/units/neutral_zurael.png"
}


// ---------------------------------------------------------------------------

var getXML = function() {
  console.log("getXML")
  return request({url: card.plist, gzip: true})
}

var writeXML = function(file){
  console.log("writeXML")
  return fs.writeFile("./lib/card.plist", file)
}

var getSprite = function() {
  console.log("getSprite")
  return request({url: card.sprite, gzip: true, encoding: "binary"})
}

var writeSprite = function(file) {
  console.log("writeSprite")
  return fs.writeFile("./lib/card.png", file, "binary")
}

var parseXML = function() {
  console.log("parseXML")
  var obj = plist.parse(fs.readFileSync("./lib/card.plist", "utf8"))
  var framename = card.slug + "_idle_000.png"
  var frame = obj.frames[framename].frame
  var values = frame.replace(/[{}]/g, "").split(",")
  var data = { x: values[0], y: values[1], width: values[2], height: values[3] }
  return data
}

// Not returning a promise due to chained methods
var cropImage = function(data){
  console.log("cropImage")
  return gm("./lib/card.png")
    .crop(data.width, data.height, data.x, data.y)
    .write("./lib/avatar.png", function(error){
      if (!error) {
        fs.unlink("./lib/card.plist")
        fs.unlink("./lib/card.png")
        console.log("Image Created")
      }
    })
}


// ---------------------------------------------------------------------------

getXML()
  .then(writeXML)
  .then(getSprite)
  .then(writeSprite)
  .then(parseXML)
  .then(cropImage)
  .catch(function(error){
    console.log(error)
  })
  .done()

这实际上是按原样工作的。我正在寻求一些帮助,将其转换为适用于对象数组的东西。我需要一种方法来传递它们并让它按顺序运行(或者如果它们都将立即运行,则更具弹性)。

任何建议都会有所帮助,因为我是新手,但完全难以让它发挥作用。谢谢!

【问题讨论】:

  • 这些函数实际上都没有返回承诺,当使用promisifyAll 时,您需要调用创建的新方法:fs.writeFileAsyncfs.readFileAsyncgm(…).….writeAsync

标签: node.js promise bluebird


【解决方案1】:

您正在使用的request-promise 模块确实将普通的request 调用转换为使用承诺而不是回调。但是,Bluebird 的 Promise.promisifyAll() 工作方式不同。它使诸如fs.writeFile() 之类的方法的正常回调版本与它们完全一样。

相反,它添加了那些返回承诺的函数的新版本。默认情况下,新版本与原始版本具有相同的名称,并在末尾添加了“Async”,因此fs.writeFileAsync() 是返回承诺的内容。

因此,您必须使用适当的方法名称才能使用 Promise:

所以改变这些:

var writeXML = function(file){
  console.log("writeXML")
  return fs.writeFile("./lib/card.plist", file)
}

var writeSprite = function(file) {
  console.log("writeSprite")
  return fs.writeFile("./lib/card.png", file, "binary")
}

对这些:

var writeXML = function(file){
  console.log("writeXML")
  return fs.writeFileAsync("./lib/card.plist", file)
}

var writeSprite = function(file) {
  console.log("writeSprite")
  return fs.writeFileAsync("./lib/card.png", file, "binary")
}

然后,您必须将cropImage() 转换为实际使用promise 逻辑并返回promise。

var cropImage = function(data){
  console.log("cropImage")
  return gm("./lib/card.png")
    .crop(data.width, data.height, data.x, data.y)
    .writeAsync("./lib/avatar.png").then(function() {
        fs.unlink("./lib/card.plist")
        fs.unlink("./lib/card.png")
        console.log("Image Created")
    });
    // Note: You are missing error handling for writeAsync
}

这应该允许你这样做:

getXML()
  .then(writeXML)
  .then(getSprite)
  .then(writeSprite)
  .then(parseXML)
  .then(cropImage)
  .then(function() {
      // done successfully here
  }, function(err) {
      // error here
  })

注意:parseXML() 中仍有同步文件 I/O,可以转换为使用异步 I/O。以下是异步文件 I/O 可能会返回一个适用于您当前方案的承诺的情况:

var parseXML = function() {
  console.log("parseXML")
  return fs.readFileAsync("./lib/card.plist", "utf8").then(function(file) {
      var obj = plist.parse(file);
      var framename = card.slug + "_idle_000.png"
      var frame = obj.frames[framename].frame
      var values = frame.replace(/[{}]/g, "").split(",")
      var data = { x: values[0], y: values[1], width: values[2], height: values[3] }
      return data
  });
}

【讨论】:

  • 谢谢!我没有意识到promisifyAll 实际上使用了不同的方法名称。您提到将 parseXML 转换为异步,考虑到cropImage 正在使用来自 parse XML 的数据,这不是我想要避免的吗?
  • @nheinrich - 我在答案的末尾添加了 parseXML() 的异步版本。如果这回答了您的问题,请单击答案左侧的绿色复选标记接受答案。如果不是,请解释尚不清楚的地方。
  • 谢谢@jfriend00!您的评论确实帮助我解决了一些我不知道的问题,但实际的问题是能够将一些东西传递到这个链中,这样我就可以遍历一组对象。基本上,当我遍历一个数组并传递一个可用于每个函数的对象时,我该如何调用它?这有意义吗?
  • @nheinrich - 当您链接承诺时,您可以将一个对象传递给第一个函数,然后将该对象作为已解决承诺的一部分返回。然后将其传递给链中的下一个函数。此外,要花 6 天的时间来回复,很难让任何形式的思考过程继续下去。
  • 抱歉延迟回复我只有晚上和周末来处理这个问题。我会给你答案,因为你已经帮助了很多。不过,我认为我会为此使用回调,因为它并没有真正按预期工作(即使在异步修复之后),而且代码也不多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-04
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-24
  • 2014-02-13
相关资源
最近更新 更多