简体   繁体   中英

How to Promisify node.js net.connect (with bluebird)?

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 complex for such a simple thing. Is there a better way? Has somebody already done this?

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:

  1. var connect_args = arguments probably won't work since arguments 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); .

  2. In this line, net.connect.apply(this, connect_args); , I don't think this 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 use net.connect.apply(net, connect_args); which will more directly simulate calling net.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 comments.

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.

PS 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.

All in all if you look at it directly - EventEmitters ings are a very complicated 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:

  1. Enter the try block
  2. Run foo with argument bar
  3. Log the value unless there was an error
  4. 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 :)

The solutions I've come 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 come 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 come across any better suggestions than what we've independently invented, so if you do, please share.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM