I'd like a Promise version of node.js function net.connect
. The Promise should be resolved with the socket if connection is successful, rejected with error if there's a connection error and preferably it should be cancellable as well, where cancellation would stop the connection attempt.
I did a quick try myself, but have not implemented cancellation yet:
function connectAsync() {
var connect_args = arguments;
return new Promise(function (resolve, reject) {
var socket = net.connect.apply(this, connect_args);
socket.once('connect', function () {
socket.removeListener('error', reject);
resolve(socket);
});
socket.once('error', function (err) {
socket.removeListener('connection', resolve);
reject(err);
});
});
}
However, this seems to be awfully plex for such a simple thing. Is there a better way? Has somebody already done this?
I'd like a Promise version of node.js function net.connect
. The Promise should be resolved with the socket if connection is successful, rejected with error if there's a connection error and preferably it should be cancellable as well, where cancellation would stop the connection attempt.
I did a quick try myself, but have not implemented cancellation yet:
function connectAsync() {
var connect_args = arguments;
return new Promise(function (resolve, reject) {
var socket = net.connect.apply(this, connect_args);
socket.once('connect', function () {
socket.removeListener('error', reject);
resolve(socket);
});
socket.once('error', function (err) {
socket.removeListener('connection', resolve);
reject(err);
});
});
}
However, this seems to be awfully plex for such a simple thing. Is there a better way? Has somebody already done this?
Share Improve this question edited Dec 25, 2014 at 11:15 Benjamin Gruenbaum 277k89 gold badges520 silver badges517 bronze badges asked Oct 19, 2014 at 7:02 NakedibleNakedible 4,1687 gold badges36 silver badges42 bronze badges 10- 1 I would not remend using a promise for this. – Benjamin Gruenbaum Commented Oct 19, 2014 at 7:04
- Agree with @BenjaminGruenbaum, you can use event handling anyway here, why you need promises? Just write connect, and catch events as you do it inside of promise. – Zav Commented Oct 19, 2014 at 7:06
-
1
I want to write
.then(connect()).then(doThing()).then(close())
style sequences. This seems to be standard practice for DB access wheredb.connect
is promisified - this is shown even in bluebird examples. Why isnet.connect
so different? – Nakedible Commented Oct 19, 2014 at 7:10 - May be i'm wrong, but all this .then() methods will call simultaneously. – Zav Commented Oct 19, 2014 at 7:13
- 1 I'll post an answer next week if you're still stuck as well as why Bluebird doesn't and won't support promo sifting event emitters automatically. – Benjamin Gruenbaum Commented Oct 19, 2014 at 16:40
3 Answers
Reset to default 2All in all if you look at it directly - EventEmitters ings are a very plicated abstraction.
Promises represent sequencing operations - think of them as the assignment operator or a semicolon. Code in regular sync programming looks something like:
try{
var value = foo(bar);
log(value);
} catch(e){
// handle error
}
Things run one after the other:
- Enter the try block
- Run
foo
with argumentbar
- Log the value unless there was an error
- If there was an error, handle it.
It's like a long single chain of operations. A promise is exactly like this:
fooAsync(bar).
then(log).
catch(function(){
// handle error
});
A promise is a chain. You can create several such chains, which is similar to other forms of concurrency (like threads) which represent executing a sequence of actions. It looks something like the following:
--------------------------------+-Success------------------>
--Error---->// might join up
On the other hand - an event emitter has no guarantees about the name or type of events it fires, the node EventEmitter has some cool features baked in (like stack traces and error
events) but there is a much weaker convention than there is with promises - different event emitters fire different events, an event emitter can do something like this:
----Foo fired-+-Handler 1 ---- Bar fired-+ ---- Baz Fired-+-Handler 1 --Handler 2 --Handler 2
It is not a single chain - so while there have been several attempts and discussions about this - no generic way to represent promises from event emitters exists - they're simply too different in the event handling and the event name.
On the other hand - pg.connect
takes a node style err-callback. So it's easy to promisify, those are very well specified and abide to a contract.
What you have is fine, and you can generalize it to an event emitter with two events. Remember you write this sort of boilerplate once and then use it all over your code :)
You can remove the two removeListener()
lines. Promises can only be resolved or rejected once so you don't have to worry about your events getting called again. The promise won't change it's state once it is fullfilled.
And, I think you have a couple issues to fix:
var connect_args = arguments
probably won't work sincearguments
is a funky type of temporal object. The usual work-around is to make a copy of it's contents:var connect_args = [].slice.call(arguments);
.In this line,
net.connect.apply(this, connect_args);
, I don't thinkthis
will be the right value because you're inside the promise callback at that point (perhaps it doesn't matter in this particular case). It would likely be more technically correct to usenet.connect.apply(net, connect_args);
which will more directly simulate callingnet.connect(args)
.
As for the wisdom of using promises for this at all, it looks like you have a couple of opinions on the matter in the ments.
Other than removing the removeListener()
lines of code, I don't think there's really much of a way to simplify this. You're creating a promise to respond to two different custom conditions so you have to write the code to detect those two conditions. No way around that.
P.S. If you don't remove the removeListener()
lines of code, you may hav an error because you're setting up an event for 'connect'
, but doing a removeListener('connection
). Also, I don't know why you're passing a function to removeListener()
because it's not the same function reference you used when the event handler was established.
The solutions I've e up with match what you've got nearly identically:
p = new Promise((resolve, reject) ->
listener = (data) ->
try
check_data_format(data)
catch err
return reject(err)
if is_right_data(data)
return resolve()
ee.on("stdout", listener)
)
return p
and occasionally when things get more unpleasant:
reject_f = null
resolve_f = null
p = new Promise((resolve, reject) ->
reject_f = reject
resolve_f = resolve
)
listener = (data) ->
try
check_data_format(data)
catch err
return reject(err)
if is_right_data(data)
return resolve()
ee.on("stdout", listener)
I filed an issue about this (asking for documentation) here but got redirected to your question.
I've e to the conclusion that the current intersection of promises and event emitters is just ugly and I have to live with it. I've not e across any better suggestions than what we've independently invented, so if you do, please share.