Reputation: 10095
The following code outputs the port number 40001 twice, then exits with error:
Error: listen EADDRINUSE
I believe this is because the code is asynchronous and does not wait for one whole iteration of the foreach loop before assigning another port.
Which Node idiom should I use to solve this? I want to loop over an array and make a phantomJS connection on a different port on each iteration.
posts.forEach(function(element, index, posts){
portscanner.findAPortNotInUse(40000, 60000, 'localhost', function(err, freeport) {
if(err) {
console.log(err);
}
console.log(freeport); //outputs 40001 twice then exits
phantom.create({'port': freeport}, function(ph){
return ph.createPage(function(page) {
return page.open("http://www.target.com/showthread.php?t="+posts[index].post_id, function(status) {
console.log("opened post? ", status);
return page.get('plainText', function(content){
console.log(content);
return ph.exit();
});
});
});
});
});
});
Upvotes: 0
Views: 174
Reputation: 61
I recently ran into the same issue and fixed it by using Math.Random to generate a start port value and just added 500 to get the end port value.
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
var startPort = getRandomInt(3000, 8000);
var endPort = startPort + 500;
portscan.findAPortNotInUse(startPort, endPort, '127.0.0.1', function (err, ports) {
.....
}
Upvotes: 1
Reputation: 22956
So you are right. The problem here is that when you find the port on the first iteration (40001) you don't consume it before the next iteration, so 40001 is found twice.
So there are 2 approaches here. Either you use a continuation, i.e. you create a next step to perform as a function, and then call the continuation after you have consumed the port.
In this case it's probably easier just to keep a track of the port though. Start by defining a base port at 40000, when you find a port, set the base port to freeport + 1
and search on that range.
var startPort = 40000
var endPort = 60000
posts.forEach(function(element, index, posts){
portscanner.findAPortNotInUse(startPort, endPort, 'localhost', function(err, freeport) {
if(err) {
return console.log(err);
}
console.log(freeport);
startPort = freeport + 1 // correct the start port to search from
phantom.create({'port': freeport}, function(ph){
return ph.createPage(function(page) {
return page.open("http://www.target.com/showthread.php?t="+posts[index].post_id, function(status) {
console.log("opened post? ", status);
return page.get('plainText', function(content){
console.log(content);
return ph.exit();
});
});
});
});
});
});
Continuation using continuable-series (not tested but should give you an idea)
var phantom = require("phantom")
var portscanner = require("portscanner")
var series = require("continuable-series");
module.exports = function processPosts(posts) {
var callInSeries = posts.map(function (post) {
return phantomPage.bind(null, post)
});
series(callInSeries, function (err) {
if (err) {
console.log(err)
}
}
}
function phantomPage(post, callback) {
portscanner.findAPortNotInUse(40000, 60000, 'localhost', function(err, freeport) {
if (err) {
return callback(err);
}
phantom.create({'port': freeport}, function(ph){
// Now that we have opened the port, we can callback immediately
// starting the next iteration
callback(null)
return ph.createPage(function(page) {
return page.open("http://www.target.com/showthread.php?t="+post.post_id, function(status) {
console.log("opened post? ", status);
return page.get('plainText', function(content){
console.log(content);
return ph.exit();
});
});
});
});
});
}
Upvotes: 3