使用Promise保存多个文件

我有一个节点URL(使用Express创build),可用于下载地址的静态图像。 因此,调用应用程序调用/下载url,并使用json传递多个地址,然后下载服务将调用Google Maps,并将每个addreses的静态图像保存在节点服务器上(然后将其发送callback用应用程序,但为了这个问题的目的,我只有在节点服务器保存图像感兴趣)。

最初,我们只关心保存地址的卫星视图,所以我写了这个代码

var request = require('request'); function saveMap(addressJson) { return Promise.all( //iterate over address json, get each address, call the Google Maps URL and save it. addressJson.map(function(item) { return new Promise(function (resolve, reject) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype=roadmap&markers=size:mid|color:red|' + item.address; request(mapUrl) .pipe(fs.createWriteStream('/temp/' + item.id + '.jpg')) .on('finish', function () { resolve("Promise resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }); }) ) } 

保存每个地址的Promise被包装在Promise.all(..)中,因为我想要saveMap()在所有地图完成下载时返回(这样我可以压缩它们并发送到调用应用程序,所以需要确保一切都已经下载)。

现在,我们需要扩展这个function,还包括卫星地图。 我希望,在同样的json迭代中,我可以有另一个可以下载卫星地图的Promise。 像这样的东西

 function saveMap(addressJson) { return Promise.all( //iterate over address json, get each address, call the Google Maps URL and save it. addressJson.map(function(item) { return new Promise(function (resolve, reject) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype=roadmap|' + item.address; request(mapUrl) .pipe(fs.createWriteStream('/temp/r/' + item.id + '.jpg')) .on('finish', function () { resolve("Promise resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }); return new Promise(function (resolve, reject) { var mapUrl2 = 'http://maps.googleapis.com/maps/api/staticmap?maptype=satellite|' + item.address; req(mapUrl2) .pipe(fs.createWriteStream(fs.createWriteStream('/temp/s/' + item.id + '.jpg')) .on('finish', function () { resolve("Promised resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }); }) ) } 

但是,这并不像预期的那样工作。 我没有收到错误,但没有生成所有的jpg文件。 有人可以帮助我了解我做错了什么,以及如何纠正这个问题。

我会做 :

  1. 用所有的承诺产生一个数组。
  2. 使用Promise.all()调用一组完整的promise

看看它的外观:(注意使用spread运算符连接两个数组):

 function saveMap(addressJson) { return Promise.all([ //iterate over address json, get each address, call the Google Maps URL and save it. ...addressJson.map(function(item) { return new Promise(function (resolve, reject) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype=roadmap|' + item.address; request(mapUrl) .pipe(fs.createWriteStream('/temp/r/' + item.id + '.jpg')) .on('finish', function () { resolve("Promise resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }) }), //End Array#map ...addressJson.map(function(item) { return new Promise(function (resolve, reject) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype=satellite|' + item.address; req(mapUrl) .pipe(fs.createWriteStream(fs.createWriteStream('/temp/s/' + item.id + '.jpg')) .on('finish', function () { resolve("Promised resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }) }), //End Array#map ])//End Promise#all } 

编辑

好的,你可能认为不需要两次迭代,因为Array#reduce

 function saveMap(addressJson) { return Promise.all( //iterate over address json, get each address, call the Google Maps URL and save it. addressJson.reduce(function(array, item) { array.push(...[ new Promise(function (resolve, reject) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype=roadmap|' + item.address; request(mapUrl) .pipe(fs.createWriteStream('/temp/r/' + item.id + '.jpg')) .on('finish', function () { resolve("Promise resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }), new Promise(function (resolve, reject) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype=satellite|' + item.address; req(mapUrl) .pipe(fs.createWriteStream(fs.createWriteStream('/temp/s/' + item.id + '.jpg')) .on('finish', function () { resolve("Promised resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }) ]); //Array#push return array; }, []); //Array#reduce ) //End Promise#all } 

DEMOS

 var length = 10; var arr1 = Array.from({length}, (el, i)=> new Promise( re => re(i) ) ); var arr2 = Array.from({length}, (el, i)=> new Promise( re => re(i) ) ); Promise.all([...arr1, ...arr2]).then(console.log.bind(console)); 

在代码中有两个return语句,这就是为什么它不起作用。

但是你真正应该开始的是抽象出你的下载的promisification :

 function download(url, target) { return new Promise(function (resolve, reject) { request(uUrl).pipe(fs.createWriteStream(target)) .on('finish', resolve) .on('error', reject) }); } 

现在你可以使用两次了。 简单地说,我不会尝试迭代你的数组只是一次,而只是map两次,并concat结果。 替代scheme是concatMap或顺序每个项目的承诺。

 function saveMap(addressJson) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype='; return Promise.all(addressJson.map(function(item) { return download(mapUrl + 'roadmap|' + item.address, '/temp/r/' + item.id + '.jpg'); }).concat(addressJson.map(function(item) { return download(mapUrl + 'satellite|' + item.address, '/temp/s/' + item.id + '.jpg'); }))); } 

再次使用Promise.all()

 function saveMap(addressJson) { return Promise.all( //iterate over address json, get each address, call the Google Maps URL and save it. addressJson.map(function(item) { return Promise.all([ new Promise(function (resolve, reject) { var mapUrl = 'http://maps.googleapis.com/maps/api/staticmap?maptype=roadmap|' + item.address; request(mapUrl) .pipe(fs.createWriteStream('/temp/r/' + item.id + '.jpg')) .on('finish', function () { resolve("Promise resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }), new Promise(function (resolve, reject) { var mapUrl2 = 'http://maps.googleapis.com/maps/api/staticmap?maptype=satellite|' + item.address; req(mapUrl2) .pipe(fs.createWriteStream(fs.createWriteStream('/temp/s/' + item.id + '.jpg')) .on('finish', function () { resolve("Promised resolved"); }).on('error', function (error) { reject('Error in creating map', error); }) }) ]); }) ) }