Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore Promises in addition to callbacks #991

Open
reconbot opened this issue Nov 3, 2016 · 16 comments
Open

Explore Promises in addition to callbacks #991

reconbot opened this issue Nov 3, 2016 · 16 comments
Labels
feature-request Feature or Enhancement
Milestone

Comments

@reconbot
Copy link
Member

reconbot commented Nov 3, 2016

Usurps #261

We'll be dropping older nodes around version 6, we currently polyfill promises for version 4, so we got them to use.

We'd need tests along with any PR

@reconbot reconbot added backlog feature-request Feature or Enhancement labels Nov 3, 2016
@reconbot
Copy link
Member Author

reconbot commented Nov 6, 2016

A problem with promise based apis is that it is not compatible with the optional callback / error event style approach we've adopted;

// Current api
let port = new SerialPort('/bad-port', {autoOpen: false});
port.on('open', () => console.log('yay the port is open!'))
port.on('error', () => console.log('boo we had an error!'))
port.open();  // no callback
// boo we had an error!

port.open(function(err) { if (err) { console.log("I'm dealing with this error") }});
// I'm dealing with this error

In node 6x+ unhandled promises throw globally, in all nodes unhandled error events throw globally. We're still going to support 4x. This might not be something we need to worry about.

So if also always return a promise or only return a promise when not given a callback, and we don't handle it's rejection some nodes will throw. And if we don't provide a callback and don't handle the error event, then all nodes will throw.

All our functions throw when given bad arguments, there seems to be a consensus around rejecting promises instead.

I think this all means that we'd have to switch wholesale to promises instead of callbacks.

@reconbot
Copy link
Member Author

reconbot commented Nov 6, 2016

There is an alternative approach which we can take, and that is to do what nodejs core is eventually going to do. Providing functionAsync equivalents that return promises.

nodejs/node#5020

That's probably the way to go.

@reconbot
Copy link
Member Author

This isn't on the roadmap for now.

@adamreisnz
Copy link

Hi @reconbot , any chance of revisiting this, or accepting PR's for work on this?

Currently we are forced to use Bluebird to promisify the port object, and call port.openAsync etc. This is of course a bit redundant, especially now that Native promises have landed in Node and are pretty much mainstream.

When I rewrote the Node API for Sendgrid, we also introduced promise support while maintaining callbacks for backwards compatibility. I see the issue with not passing a callback and not handling rejected promises, which could trip up apps using callbacks. However, given the speed at which you are releasing major versions with other breaking changes, it's conceivable that a new major version is released that either always expects promises/async to be used.

Alternatively, have you considered a flag that enabled promises globally (or during instantiation of the SerialPort object)?

const port = new SerialPort(port, {
  baudRate,
  autoOpen: false,
  usePromises: true,
});

This could even be the default setting, with optional false value indicating the current style of callbacks only usage.

Thoughts?

@reconbot
Copy link
Member Author

reconbot commented Aug 31, 2017

The next major version is unfortunate. It fixes some major windows performance issues and changes our binary distribution system. Doing the bump seemed worth it even though there's only potential behavior changes.

As for promises. I started using them for the bindings but one of the goals was to match the node stream api as close as possible. In fact most of the "magic" is in the bindings. I'm doing an exploration of other kinds of streams (whatwg/streams, most) and async iterators. But I doubt we'd switch this project to any of them, but instead share the bindings with other packages. (and probably split them into their own package)

So async methods for node serialport? The convention doesn't exist for node streams but we snuck it into #list().

  • .set(), .get(), .flush(), .drain(), .update(), .open(), and .close() could return a promise if no callback is provided without issue.
  • .read() and .write() already have useful return values and are stream methods. A .writeAsync() would be trivial but a .readAsync() would be difficult to do and would only work on paused streams. I've tried to write a utility function .readAsync() for use on generic streams a few times.
  • .pause() and .resume() are sync and also stream methods, no changes there.

It seems that node core is moving towards the .fooAsync() style of promised based callbacks. I think it makes sense to stay in "spec". I want to have more discussion about it but I think it makes sense to accept PRs with async prefixed methods. (We should probably fix .list() and make a .listAsync() too.)

PS I'd love to make a non node-streams version of serialport. And I love async/await, a lot.

@reconbot reconbot reopened this Aug 31, 2017
@adamreisnz
Copy link

Thanks for the detailed comments!

Since I use the .on('data') event instead of .read(), for me the main benefit would arise from making .open() and .close() return promises. If the methods you mentioned in your first bullet point could return promises when no callback is provided that'd be great.

I wouldn't mind if the API is split between callback and non-callback functions, but I think the naming would be misleading. E.g. .open() will still take a callback, and it will not be sync, but it might be implied by the presence of an .openAsync().

So I would prefer the approach where .open() returns a promise when no callback is provided. Can probably leave read, write, pause, resume as they are for now.

And I love async/await, a lot

Amen!

@reconbot
Copy link
Member Author

reconbot commented Sep 1, 2017

I'm wrong on the fooAsync() in core. They seem to be landing on a module namespace. eg require('serialport/promise') but I'm not 100% on that.

@adamreisnz
Copy link

adamreisnz commented Sep 1, 2017

I see, interesting read. I guess their main concern is maintaining backwards compatibility with callbacks. It also looks like the discussion ended about a year ago.

@reconbot
Copy link
Member Author

reconbot commented Sep 4, 2017

For me it's not backwards compatibility it's principal of least surprise. I'd like node serialport to work like everything else does.

@adamreisnz
Copy link

Someone needs to be the trend setter ;)

@Ks89
Copy link

Ks89 commented Jun 21, 2018

Everything in NodeJs is moving to promises, so I think that a new major release non-backward compatible is acceptable.

@reconbot reconbot added this to the Next milestone Sep 15, 2018
@reconbot
Copy link
Member Author

I'm 💯 of a different mind these days about this. We still support node 6 which doesn't have async functions but I'd be willing to transpile to ignore that fact.

@Ks89
Copy link

Ks89 commented Sep 15, 2018

Nodejs 6 has a scheduled eol date at April 19.
So, it could be the right moment to switch to node 8 updating the code.

Yes, with babel the "compilation" is very easy.

If you need beta testers when you want to update the code with promises/asyncawait feel free to ask, I'm happy to help you. I'm using this module in a project wrapping every api that I'm using in promises.

@reconbot
Copy link
Member Author

reconbot commented Sep 16, 2018 via email

@Ks89
Copy link

Ks89 commented Sep 20, 2018

Yes, I suppose that it's impossible to promisify a stream

@reconbot
Copy link
Member Author

Alright, what do you all think about this? #1850

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-request Feature or Enhancement
Development

Successfully merging a pull request may close this issue.

3 participants