-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
v2: Can of Promises #3729
base: master
Are you sure you want to change the base?
v2: Can of Promises #3729
Conversation
var success = options.success || _.identity; | ||
var error = options.error; | ||
delete options.success; | ||
delete options.error; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't modify the options
object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err, but the master branch does modify the options
object already... I guess you object to the delete
lines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, but I'm not modifying the object that is provided in the arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you care to delete these properties? It shouldn't matter if they stick around, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want them called by ajax (i.e. jQuery or whatever it might be), because I want them to be apart of the promise chain.
I'm sympathetic to this changeset and think a movement like this may be a good way to move backbone forward (along with @jmeas' proposed router changes) in v2. So a hesitant 👍 after this has been carefully reviewed |
I know ECMA wouldn't break the internet, but one day `catch` might be a reserved word. On the suggestion of @megawac
I see no need to refer to `Backbone.Promise` everywhere since the reference from the arguments list isn't going to disappear.
The Promise contract with Backbone is only two function, `then` and `catch`, that both return another `Promise`. On the suggestion of @megawac, see jashkenas#3729 (comment)
}; | ||
if (isPromise(defaults)) | ||
return defaults.then(consume); | ||
return consume(defaults); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about returning a promise from initialize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👎 on this. Initialize is always synchronous, therefore this will always be resolved after you've created the instance -- in other words, exactly where you are with the current Backbone. Revert this please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have real world use cases where initialize is not synchronous. Of course, the constructor will not return a Promise
if the user never returns a Promise
... but I can tell that you feel strongly about this and I'm happy to agree to disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would love to hear those use cases if you feel like sharing.
On Mon, Jul 27, 2015, 9:06 AM Corin Lawson [email protected] wrote:
In backbone.js
#3729 (comment):
- this.changed = {};
- this.initialize.apply(this, arguments);
- var model = this, args = slice.call(arguments);
- var defaults = _.result(this, 'defaults');
- var consume = function(defaults) {
attrs = _.defaults({}, attrs, defaults);
model.set(attrs, options);
model.changed = {};
var init = model.initialize.apply(model, args);
if (isPromise(init))
return init.then(_.constant(model));
return model;
- };
- if (isPromise(defaults))
return defaults.then(consume);
- return consume(defaults);
I have real world use cases where initialize is not synchronous. Of
course, the constructor will not return a Promise if the user never
returns a Promise... but I can tell that you feel strongly about this and
I'm happy to agree to disagree.—
Reply to this email directly or view it on GitHub
https://github.com/jashkenas/backbone/pull/3729/files#r35532739.
Adam K (mobile)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd rather let the user handle this themselves -- either with a custom wrapper or waiting for the promise to resolve
defaults.then(attrs => new Backbone.Model(attrs)
.then(model => {
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have reverted the commit that affects the constructors, and I hinted at a use case in the last sentence of my OP. I would like to create an example to illustrate why I started on this course in the first place, but that will take time to prune back some real world code into something bite-sited :)
I get 7 failed tests when I use either this branch or the jashkenas/master branch. |
} | ||
|
||
}(function(root, Backbone, _, $) { | ||
}(function(root, Backbone, _, $, Promise) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of requiring a particular Promise implementation. Many people will end up requiring both the library you have chosen here and whatever Promise library they actually use.
In my libs I prefer doing:
if (typeof Promise !== 'undefined') {
Backbone.Promise = Promise;
}
That way, if someone's using native Promises, and they wish to continue using native promises, they can. Or if they'd rather use RSVP, they can do that, too. And so on.
An extra install instruction – telling people to configure Backbone.Promise
– is worth the added benefit of flexibility, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree... I felt dirty trying to pick an implementation from npm, bower, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. Let's stick with the global Promise, or throw an error if one isn't defined (see @jridgewell's branch).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the dependency on any one promise implementation but still attempt to load it via require (or fallback to global)... Do you see any problems with that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes... if your project didn't include the promise
library this bit of code would fail. It's much better to require the dev to provide a Promise polyfill or set one on Backbone.Promise
directly instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but still attempt to load it via require (or fallback to global)... Do you see any problems with that?
Yeah, that is still going to be a problem for people. You should remove everything related to promises from both package.json
, bower.json
, and from the UMD wrapper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I thought that the UMD wrapper would simply receive undefined
... no probs
Also, there is significant overlap with #3651 |
I think this pull really showcases some of the downsides of Promises (and asynchronicity in your code in general) in that once you use them in a few places, you practically have to make your entire API asynchronous. I see very little reason for I'm fully in support of wrapping asynchronous calls (sync is the biggest one) in Promises, however. |
Ha! Perhaps I should have added: "WARNING: May cause Scoliosis" But on a serious note, I don't like the So I agree that |
Sorry, that close was a slip of the thumb... |
Ha. @paulmillr made a fork of Backbone a while back called Scoliosis (since renamed Exoskeleton). Maybe we could implement it there first.
Right of course. But it starts to get ugly fast when you mix sync and async code using promises. In my experience they tend to take over your entire application when really you should've been writing it reactively in the first place.
There's power but there's also a great deal of sloppiness. You know what Ben Parker (and Teddy Roosevelt) said about power and responsibility...
Can you give an example? |
I think if we want to return a promise it would be good to pass parsed response to then's callback. Read more at #3779 |
The |
I'm a big fan of Backbone and of Promises, this is my take on combining the two.
I understand that this may be a can of worms and I have changed many, many functions and it may not be your cup of tea. For this reason I have also made sure all the tests pass (with changes, of course) and I have tried to be backwards compatible (the Todos app still works). I haven't added any new tests yet but I would like to, as use cases arise and as I start to use this code in my own projects.
I have many use cases in mind and have already started with my own major changes: for instances Router#execute will catch errors and trigger an error event (on the router). My intention is that route actions should return the promise(s) from other methods (like render), this will mean that no error gets swallowed or forgotten about.
I'm making this PR, ignorant of the opinions of others (I've only just realised that there are already discussions about this) but keen to learn those opinions and to get some feedback.
I have made some assumptions:
Promise
, as per ES6, is a global object. In my view it's very easy to include the standard polyfill.jQuery.Deferred
is notPromise
and needs to be avoided.That last point is important to me. I am currently working on a big project that uses requirejs and pouchdb/couchdb. We use promises to orchestrate module dependencies as well as data from the server, whereby a Promise is created inside initialize and
then
is called inside render.