Reputation: 2085
I have a Node URL (created using Express) that can be used to download a static image of an address. So, the calling app calls the /download url and passes multiple addresses using json, then the download service would call Google Maps and save the static image of each of these addreses on the node server (it would then send it back to the calling application, but for the purpose of this question I am only interested in saving the images in the node server).
Initially, we were only interested in saving satellite view of the addresses, so I wrote this code
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);
})
});
})
)
}
The Promise for saving each address is wrapped inside Promise.all(..), since I want to the saveMap() to return when all maps have finished downloading (so that I can zip them and send to the calling app, so need to be sure that everything has been downloaded).
Now, we need to extend this function to also include satellite maps. I was hoping, that within same json iteration, I can have another Promise which would download satellite maps. Something like this
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);
})
});
})
)
}
However, this does not work as expected. I don't get an error but it is not generating all the jpg files. Can someone please help me understand what I am doing wrong and how to get this corrected.
Upvotes: 2
Views: 3518
Reputation: 664395
You have two return
statements in that code which is why it does not work.
But what you really should start with is to abstract out your promisification of the download:
function download(url, target) {
return new Promise(function (resolve, reject) {
request(uUrl).pipe(fs.createWriteStream(target))
.on('finish', resolve)
.on('error', reject)
});
}
Now you can use that twice. For simplicitly, I would not try to iterate your array just once but simply map
it twice and concat the results. The alternative would be to concatMap
or sequence the promises of each item.
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');
})));
}
Upvotes: 2
Reputation: 3683
I would do :
Look how it looks : (Note the use of spread
operator to concatenate both arrays) :
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
}
EDIT
Okay, You may think there's no need for two iterations, because of 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));
var length = 10;
var arr1 = Array.from({length}, (el, i)=> i );
var finalArr = arr1.reduce((array, item)=>{
array.push(...[
new Promise( re => re(item) ),
new Promise( re => re(item*2) )
]);
return array;
}, []);
Promise.all(finalArr).then(console.log.bind(console));
Upvotes: 2
Reputation: 17168
Just use Promise.all()
again:
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);
})
})
]);
})
)
}
Upvotes: 0