-
Notifications
You must be signed in to change notification settings - Fork 591
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
core: add support for promises #1142
Conversation
Go team promises, go! |
@@ -1,5 +1,5 @@ | |||
# Google Cloud Node.js Client | |||
> Node.js idiomatic client for [Google Cloud Platform](https://cloud.google.com/) services. | |||
> Node.js idiomatic client for [Google Cloud Platform](https://cloud.google.com/) services, now with more promises! |
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
re:
if possible you can allow [optional] callbacks and return Promises, as that will help users with migration. |
@jasonswearingen -- we talked about this starting here: #551 (comment) and landed on just promises. |
@callmehiphop I think we should update this with the state of the world now. We are no longer removing callback support, right? |
@stephenplusplus that is correct, the plan is now to move streams behind a // stream mode
bucket.getFiles({ stream: true })
.on('data', function() {});
// promise mode
bucket.getFiles()
.then(function() {});
// callback mode
bucket.getFiles(function() {}); I'll update the PR description accordingly. |
It's so great to see this PR after read the long ISSUE #551 Do we have a date of when the gcloud-node with promise support will be published on NPM? I can't waiting for it. 👍 |
@callmehiphop , I'm a bit worried about the // Promise mode
bucket.getFiles()
.then(function(files) { });
// Maybe we can let people create streams from promises (?)
bucket.getFiles().createStream()
.on('data', function(file) { });
// Or if we can make it even shorter, promises also act like streams?
bucket.getFiles()
.on('data', function(file) { }); |
@zixia I share your enthusiasm! Hopefully we'll get Promises published within the next couple of months. @jgeewax I can't help but agree that The third example is pretty interesting, I'm not aware of any objects that inherit from both Promises and Streams. However, it wouldn't be very difficult to concat the stream data together and resolve via Promises - there are already several packages that do this. @stephenplusplus what are your feelings on decorating the stream object we return with a |
@callmehiphop : The only issue I can think of here is what happens when you consume stuff with a stream and then decide you want a promise? bucket.getFiles().on('data', function(file) { }).then(function(files) { }); What should that do? Error somewhere? or is it technically valid? |
I just tested that, technically it's valid and does not throw an error. We could unbind events if a user adds a promise, assuming that's a concern. |
This? typeof bucket.getFiles(callback) // undefined
typeof bucket.getFiles() // Stream
typeof bucket.getFiles().then() // Promise Aren't there more Promise methods, like bucket.getFiles()
.on('data', function(file) {})
.then(function(files) {}); I think this could definitely lead to confusion, but where there is flexibility, that's a known side effect. This is up to @callmehiphop's intended implementation, but it could either start two requests to fetch fresh data, or fight with each other for which stream (the one the user is holding or the internal |
Currently the only methods on the Promise prototype chain are That does raise a valid concern that this approach would not be very future proof if additional methods were introduced. |
That would be bad, the idea would be to re-use the already existing stream.
Technically the results would be emitted to both, so I'm not sure I understand how they would be fighting with each other? If it helps to illustrate how it works, locally (for testing) I added this to the stream.then = function(success, failure) {
return new Promise(function(resolve, reject) {
stream.on('error', reject).pipe(concat(resolve));
}).then(success, failure);
}; |
Sure it would be an extra request, but since the user is asking for a stream and a promise, it might be expected. I believe it would conflict when more complicated stream use cases are considered, like the user isn't just draining a stream with on(data) listeners, but manipulating or throttling the incoming messages. |
I think trying to accommodate multiple interfaces in this fashion is bad idea. Which unfortunately doesn't make any of those examples very practical for us. An alternative that we haven't really discussed is making a separate method for the streaming versions // callback
bucket.getFiles(function() {});
// promise
bucket.getFiles().then(function() {});
// stream
bucket.getFilesStream().on('data', function(file) {}); @stephenplusplus @jgeewax not sure what your feelings on an approach like this are - IMO the only real downside here is that the documentation would not be consolidated like it is today, but that might actually be a selling point since the user would no longer have to search through several examples to see what the streaming syntax looks like. |
I do like the docs simplification part. What about declaring up front, at On Thu, May 19, 2016, 10:47 AM Dave Gramlich notifications@github.com
|
I was going to suggest this one also, but had trouble with the @stephenplusplus : I'm not a huge fan of forcing folks to determine their usage at the start... that seems... scary. What if we tried doing this the same that fs does something like this? // callback
bucket.getFiles(function() {});
// promise
bucket.getFiles().then(function() {});
// stream
gcloud.createReadStream(bucket.getFiles())
.on('data', function(file) { }); |
When dealing with callbacks and Streams it's really obvious what a user wants, since they have to supply us with the actual callback. With Promises and Streams however, we don't have anything that allows us to distinguish one from the other - hence why we were suggesting
This is a bit more feasible than trying to combine Streams with Promises, however I think we might come across some of the same pitfalls. This does have the added benefit of not stubbing Promise methods though. From a technical standpoint, I see two options to achieve this:
e.g. var promise = bucket.getFiles();
var stream = promise.createStream();
promise.then(function(files) {});
stream.on('data', function(file) {}); We could
IMO this would have to be implemented identically to As for |
@jgeewax what's scary about the I think mixing streams and promises is confusing... even if we figure out how to solve it, the user has to figure out what we figured out, y'know what I mean? Here's my personal preference of what is the least intrusive on the user, and the most predictable:
|
I'm not a big fan of this. Lets assume a dev would want to use both streams and promises in the same app, they would either have to go back and forth between setting that flag or manage multiple objects that are more or less the same. I think this might actually be more confusing than trying to make a Stream/Promise hybrid. |
Maybe it can be treated as a global setting of |
I skimmed the recent posts, and I think an explicit also, I can imagine a scenario where you mostly use promises, but in some cases need to use streams. For example: I use promises with datastore right now (a wrapper I made) and it works great, except if I have a datastore query that returns tons of results. for that I'd use streams. regarding some global |
Thanks, @jasonswearingen. I think we should sit back until we have an implentation to review and let @callmehiphop get started on his ideal vision. This is his baby, and I trust him fully to deliver. |
Changes Unknown when pulling f1ff315 on promise-support into * on master*. |
for how long can we see a Promise API? |
@c0b I don't think this is our top priority at the moment, so it's hard to nail down an accurate estimate, but I would guess sometime within the next several months. |
Looking at the implementation of var Language = require('@google-cloud/language');
var language = Language();
var api = Language.v1beta1();
var document = {
type: 'PLAIN_TEXT',
content: 'Hello, world!'
};
// The hand-written method returns just a promise
var promise = language.detectSentiment(document.content);
// gax-nodejs returns an event emitter
var apiRequest = api.analyzeSentiment(document);
try {
// uh oh, how to I cancel the request
promise.cancel();
} catch (err) {
// Uncaught TypeError: promise.cancel is not a function
}
// Nice, this works
apiRequest.cancel();
promise
.then((sentiment) => {
// ...
})
.catch((err) => {
// ...
});
// This works today
apiRequest.result
.then((response) => {
// ...
})
.catch((err) => {
// ...
}); I'm thinking the hand-written methods should just return the event emitter returned by var languageRequest = language.detectSentiment(document.content);
var apiRequest = api.analyzeSentiment(document);
// Yay, I can cancel the request!
languageRequest.cancel();
// Nice, this works
apiRequest.cancel();
// Access the promise on the "result" property
languageRequest.result
.then((sentiment) => {
// ...
})
.catch((err) => {
// ...
});
// This works today
apiRequest.result
.then((response) => {
// ...
})
.catch((err) => {
// ...
}); Use the event emitter in combination with callback-style: var languageRequest = language.detectSentiment(document.content, function (err, sentiment) {
// ...
});
var apiRequest = api.analyzeSentiment(document, function (err, response) {
// ...
});
// Yay, I can cancel the request!
languageRequest.cancel();
// Nice, this works
apiRequest.cancel(); For those methods that optionally return a stream, the method will have to be split into two methods:
// callback-style
table.getRows(function (err, rows) {
// ...
});
// promise-style
table.getRows().result.then(function (rows) {
// ...
}).catch(function (err) {
// ...
});
// stream
table.getRowsStream()
.on('error', console.error)
.on('data', function (row) {})
.on('end', function () {
// All rows have been retrieved.
}); |
f1ff315
to
b6b018e
Compare
340f3d8
to
6899963
Compare
@callmehiphop can you update the TODOs to outline our roll out plan? Also, just throwing these out:
|
Done!
I think obviously we should emphasize this in the release notes, otherwise I'm not too sure.. the main README is fine. I've updated the param list on the umbrella package as well showing how to integrate Bluebird, so it should also be available on the default docs page. Otherwise I'm not sure where else it fits?
Yar, I'll update the READMEs as I create PRs for each service. |
Promises have been released! |
great job guys! thanks! |
This is wonderful, just what I was looking for. I bet there are others who were looking for this but never found it because 'Promise' or '.then' are nowhere to be found in the readme.md and all of those examples use the callback style. |
Well, let me make sure I can use it correctly, then I'll give it a shot ;) |
Fixes #551
This PR aims to add support for Promises.
This will also introduce a breaking change moving all stream related APIs to a separate method.
Because this will be such an enormous change and GitHub probably won't be able to display everything, logical commits will be used to make reviewing the changes a little easier.
TODO:
Documentation site support- for now we're going to rely on examples.Testing Locally
To test locally you need to link the
@google-cloud/common
package to all other modules. To do so you can runSeveral modules also have a dependency on
@google-cloud/storage
so additional links may be required.Rollout Plan
@google-cloud/common
streaming and promise updates. (common: add promise support #1693)@google-cloud/common
0.7.0
@google-cloud/storage
streaming and promise updates. (storage: add promise support #1697)@google-cloud/storage
0.3.0
@google-cloud/pubsub
and@google-cloud/bigquery
updates.@google-cloud/pubsub
and@google-cloud/bigquery
updates