2015-11-14 38 views
0

我最近開始學習使用node.js編寫應用程序,而且異步的特性給了我一些麻煩。node.js承諾用藍鳥的循環體系結構

我想寫一個腳本,通過對象數組迭代(下面的例子)。每個對象都包含一些我想要下載的文件(plist,png)的url。一旦下載,我想從一個數據中獲取一些數據,然後根據這些數據裁剪另一個數據。

我創建單個對象並將其傳遞給承諾鏈(這是我在下面提供的示例)時有此工作。我遇到的問題是當我嘗試使用Promise.each,Promise.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() 

這實際上按原樣工作。我正在尋找一些幫助,將它轉換成對象數組的東西。我需要一種方法將它們傳入並順序運行(或者如果它們都將立即運行,它們會更具彈性)。

任何建議都會有所幫助,因爲我對此很陌生,但完全努力使其發揮作用。謝謝!

+2

的那些功能無實際回報的承諾,在使用'promisifyAll'你需要調用了新方法創建:'fs.writeFileAsync','fs.readFileAsync','gm(...)。... .writeAsync' – Bergi

回答

1

您正在使用的request-promise模塊確實將正常的request調用轉換爲使用promise而不是回調。但是,Bluebird的Promise.promisifyAll()的工作方式不同。它保留了正常回調版本的方法,如fs.writeFile()就像它們一樣。

相反,它增加了那些返回承諾的函數的新版本。默認情況下,新版本與原始版本的名稱相同,但在末尾添加「異步」,因此fs.writeFileAsync()是返回承諾的內容。

所以,你必須爲了使用適當的方法名稱與承諾的工作:

所以更改這些:

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()轉換爲實際使用承諾邏輯並返回承諾。

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

注意:你還有同步文件I/O在parseXML()這可以轉換爲使用異步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 
    }); 
} 
+0

謝謝!我沒有意識到'promisifyAll'實際上使用了不同的方法名稱。您提到將parseXML轉換爲異步,考慮到cropImage使用來自解析XML的數據不會是我想要避免的事情嗎? – nheinrich

+1

@nheinrich - 我在我的答案末尾添加了'parseXML()'的異步版本。如果這回答您的問題,請點擊答案左側的綠色複選標記接受答案。如果沒有,請解釋一下還不清楚。 – jfriend00

+0

謝謝@ jfriend00! 您的評論確實幫助我理清了一些我並不知道的問題,但實際的問題是能夠將某些內容傳遞給此鏈,以便可以循環訪問一組對象。基本上,當我循環一個數組並傳遞一個可用於每個函數的對象時,我該如何調用它?那有意義嗎? – nheinrich