node.js 使用 bluebird 的 promise 循环架构
node.js promise loop architecture with bluebird
我最近开始学习使用 node.js 编写应用程序,一切的异步性质给我带来了一些麻烦。
我正在尝试编写一个遍历对象数组的脚本(下面的示例)。每个对象都包含几个我要下载的文件(plist、png)的 url。下载后,我想从一个中获取一些数据,然后根据该数据裁剪另一个。
当我创建一个对象并通过承诺链(这是我在下面提供的示例)传递它时,我有这个工作。我遇到的问题是当我尝试使用 Promise.each
、Promise.all
或 Promise.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()
这实际上是按原样工作的。我正在寻找一些帮助将其转换为适用于一系列对象的东西。我需要一种方法来传递它们并按顺序 运行(或者如果它们都将立即成为 运行,则更具弹性)。
任何建议都会有所帮助,因为我是新手,但完全难以让它发挥作用。谢谢!
您正在使用的 request-promise
模块会将正常的 request
调用转换为使用承诺而不是回调。但是,Bluebird 的 Promise.promisifyAll()
的工作方式不同。它使 fs.writeFile()
等方法的正常回调版本完全保持原样。
相反,它添加了 return 承诺的那些函数的新版本。默认情况下,新版本的名称与原始版本相同,但在末尾添加了 "Async",因此 fs.writeFileAsync()
是 return 的承诺。
因此,您必须使用适当的方法名称才能使用 promises:
所以改变这些:
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()
转换为实际使用承诺逻辑和 return 承诺。
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 的情况,return 是一个适用于您当前方案的承诺:
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
});
}
我最近开始学习使用 node.js 编写应用程序,一切的异步性质给我带来了一些麻烦。
我正在尝试编写一个遍历对象数组的脚本(下面的示例)。每个对象都包含几个我要下载的文件(plist、png)的 url。下载后,我想从一个中获取一些数据,然后根据该数据裁剪另一个。
当我创建一个对象并通过承诺链(这是我在下面提供的示例)传递它时,我有这个工作。我遇到的问题是当我尝试使用 Promise.each
、Promise.all
或 Promise.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()
这实际上是按原样工作的。我正在寻找一些帮助将其转换为适用于一系列对象的东西。我需要一种方法来传递它们并按顺序 运行(或者如果它们都将立即成为 运行,则更具弹性)。
任何建议都会有所帮助,因为我是新手,但完全难以让它发挥作用。谢谢!
您正在使用的 request-promise
模块会将正常的 request
调用转换为使用承诺而不是回调。但是,Bluebird 的 Promise.promisifyAll()
的工作方式不同。它使 fs.writeFile()
等方法的正常回调版本完全保持原样。
相反,它添加了 return 承诺的那些函数的新版本。默认情况下,新版本的名称与原始版本相同,但在末尾添加了 "Async",因此 fs.writeFileAsync()
是 return 的承诺。
因此,您必须使用适当的方法名称才能使用 promises:
所以改变这些:
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()
转换为实际使用承诺逻辑和 return 承诺。
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 的情况,return 是一个适用于您当前方案的承诺:
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
});
}