Christian
Christian

Reputation: 127

How to extend breeze to be interoperable with standard CommonJS promises spec

I have monkey patched the breeze EntityManager.prototype so that it returns angular $q promises whilst also calling $rootScope.$apply (using code similar to Ward Bell's solution).

However, this falls down in one important respect: code internal to breeze registers errorCallbacks using the fail method on the promise object (eg promise.then(callback).fail(errCallback)

The fail method is not part of the CommonJS promise/A+ specification, and as such is not included in the angularjs promise api. The consequence is that the angularjs promise that the EntityManager.prototype is now returning does not have the fail method and so an exception is thrown.

QUESTION: is there a way of customizing breezejs so as to support only the promise api included in the CommonJS/A+ spec without me having to directly modifying the breezejs library itself? As suspect not, so I have also raise a breeze change request.

Thanks Christian Crowhurst

Upvotes: 0

Views: 362

Answers (2)

John
John

Reputation: 418

It looks like the Breeze response (using a Breeze Labs disclaimer) is:

to$q - a bridge to Angular promises

"A BreezeJS asynchronous method returns a promise, typically a promise to deliver some data from a remote service (or an error if the service request fails).

Breeze methods return Q.js promises not AngularJS $q promises. The to$q plugin makes it easy to transition from a Breeze Q promise to an Angular $q promise."

Upvotes: 0

Ward
Ward

Reputation: 17863

Please do not monkey patch the breeze EntityManager prototype with $q. You are breaking the warranty when you do that. Just don't.

Breeze uses Q.js for promises and Q is CommonJS compliant. Q is a rock-solid promise implementation; $q ... not so much.

If anything you should be complaining that $q is out of compliance with the spec precisely because it calls $apply which is a side effect that is often unwanted especially in testing. Don't get me started.

The fail method is just Q sugar, an extension of then. We like its readability and we use it.

You could add a fail method to the $q promise promise if you liked. Pretty simple. Something along the lines of an alias for then(function(data){return data;}, failHandler)

You could make the case that we should not use the Q fail method internally and instead limit our use of promises inside our Breeze components to just those members identified in the CommonJS spec. I'll forward that thought internally. It would certainly facilitate the possibility of alternatives to Q. I personally dislike that Breeze has any dependencies on 3rd party libraries, even a library as stellar as Q.

Trust me, we considered this. There is one hurdle we cannot clear: most promise implementations are crap.

Breeze depends upon a promise library that behaves properly under all conditions and especially in its handling of exceptions. If we opened this door, people would start plugging in any promise library they wanted ... anything with a "then" method. Their Breeze apps would start to break in mysterious and untimely ways. We'd get calls telling us that Breeze is crap.

Case in point: jQuery. The jQuery deferred is a broken implementation. If someone used that in place of Q, Breeze apps would break. Not all of the time ... which is worse than breaking all the time.

I will not say $q is crap. I will say that it is unsound ... and not just because it always calls (or does the equivalent of calling) $apply.

Let me say again what I said at the top: please do not monkey patch the breeze EntityManager prototype with $q.

I can imagine why you want to do that. You want the promise returned from an EntityManager method to be a $q promise. Sorry. Bad idea.

Please follow my recommendation instead. Use our to$q extension to Q.js (documentation forthcoming). It's easy to "install" after which, instead of this:

var QPromise1 = someQuery.using(manager).execute();
var QPromise2 = anotherQuery.using(manager).execute().then(success, fail);

you write this:

var $qPromise1 = someQuery.using(manager).execute().to$q();
var $qPromise2 = anotherQuery.using(manager).execute().to$q(success, fail);

How hard is that?

Upvotes: 1

Related Questions