wire.js waits for modules that return promises

72 views
Skip to first unread message

Mike Macaulay

unread,
Oct 2, 2013, 11:27:35 AM10/2/13
to cuj...@googlegroups.com
Hey everyone, newbie to wire, loving it so far, but running into a snag and would like some guidance.   

I have a tricky dependency I'm trying to abstract out with a promise (my 'resolver' code is below).  I then want to inject this promise as a dependency into another module.  But Wire seems to be waiting for the promise to resolve and trying to inject the resolved value into my other component.  The problem is this promise doesn't get resolved until after wire does its thing, which is causing a timeout.  I'm trying to bring wire in incrementally so I can't at this time convert the entire app to use wire and resolve this dependency via wire.  My question is, is there a way to have wire NOT wait for the promise and simply use the promise as an object?   One way I see is to return a function that returns the promise, but this doesn't feel right.  Other ideas?  Any help would be appreciated!

Mike

define(function (require) {
    'use strict';
   var app = require('app'),
        Q = require('q');

    var deferred = Q.defer();
    app.on('initialize:after', function(){
        deferred.resolve(app.myDependency);
    });

    return deferred.promise;

});


Brian Cavalier

unread,
Oct 2, 2013, 1:32:34 PM10/2/13
to cuj...@googlegroups.com
Hey Mike,

Cool, glad you're liking wire!

This certainly sounds like an odd situation, but I understand that things like this come up when you start modifying an existing architecture to bring in something like wire.  Typically, you'd simply want to rely on wire to inject the dep when the promise resolved, but that clearly won't work here.  Wire is designed to hide init-time async drudgery :)  and in basically every case, when it encounters a promise, it'll wait for it, as you saw :)

It does that for various reasons, one of which is to try to ensure correct initialization order of all components even in the face of asynchrony.  Trying to "hide" a promise from wire is doable, but usually requires a little hoop-jumping, and can have some implications: since wire won't know about the promise, it can't consider that promise as part of it's automatic initialization ordering.  So, your app code may have to deal with that.

One quick question: is your ultimate goal to inject the promise itself, or to inject the promise's fulfillment value? As you said, waiting for the promise to fulfill causes deadlock, but just trying to get a sense for the ultimate goal.

If you need to inject the promise, the simplest thing might be to pack the promise in a simple wrapper object, e.g.

return { promise: deferred.promise }

And then unpack it in the target code.  That requires changing the code into which the dependency is injected, which is probably simple, but definitely not ideal.  For example, you could use an init method instead of property injection for that particular dependency.  Seems like we can do better ... hmmm ...

A wire plugin could certainly do it.  If you need to do this in several places, that might be worthwhile, but if it's just this one spot, a wire plugin is probably overkill.  I'll give it a bit more thought and try to post some more ideas soon.

P.S., if you're already using Q for promises, that's cool. Q is an excellent library, and since wire uses when.js internally, they'll all play nice together.  You could opt, in the future, to reduce your dependency graph by using when.js in your app code as well, since it's already an implicit dependency for wire.

Mike Macaulay

unread,
Oct 2, 2013, 1:53:51 PM10/2/13
to cuj...@googlegroups.com
Hey Brian,

Yeah, I realize it is definitely not an ideal situation, and actually trying to convert this module to wire really exposed this dependency to us and we realized it was actually a race condition, dependent on something that doesn't get initialized for some time later in the app (after everything is initialized).  So I do realize that this request is born out of a bad dependency... :)   Unfortunately, we're not yet in a position to fix this, hence this hack.  

To answer your question, I'd like to avoid a promise entirely and just send in the value, but since its not there at the time of wiring and I don't want to bring wire into the rest of our app yet, I have to use promises (or something else which is not as nice).  

I like your suggestion better.  I was using a function that returned a promise, e.g.
return function(){return deferred.promise;}
As you say, its not ideal though, now the injected module needs to know to call the function (or unwrap an object) and then wait on the promise.  

I could see a plugin if this sort of thing happens frequently.  It shouldn't on new apps I understand, but for migratory users, I could see this happening.  In any case, thanks for confirming the behavior.  I could not find it documented in the docs (which are quite good if you spend some reading them btw).  It might be worth putting a note in components.md that loading a module that returns a promise will actually cause wire to wait on that promise and propose a workaround like this so that if people want to go down my route for some reason again, they can.  

Thanks for the quick and detailed response!  Keep up the great work!  

Mike
ps.  Yep, love the Q, its spread around our whole app already though, when.js might be a contender for sure. :) 

Brian Cavalier

unread,
Oct 2, 2013, 7:32:02 PM10/2/13
to cuj...@googlegroups.com
I totally understand, Mike.  The only time things like this *don't* happen is when you're starting a completely new project from zero :)  Pretty cool, though, that working through it helped expose a race condition!

Here's another idea I just found time to write down: https://gist.github.com/briancavalier/6801964

The idea is that there is some 3rd party, in this case a "wrapped promise property injector" object or function (there's an example of each), whose responsibility it is to wait for the promise and inject its value into a target property of some target object.  The nice thing here is that the code into which the dependency is ultimately being injected need not be changed, and will be completely unaware that there are some promise shenanigans going on behind the scenes.  IOW, it completely decouples this necessary little promise hoop-jump from the application components.

You'll likely still need to manage some asynchrony to ensure that the target object doesn't try to use the injected value before the injector has injected it (how many times can I say "inject" in one sentence? 3 apparently).  That might be tricky.  One way to do it might be, instead of injecting a property, create a similar injector that calls a method on the target object, passing the promise's value as a parameter.

There's probably several variations on this theme, but hopefully that gives you some options and things to think about!

Mike Macaulay

unread,
Oct 3, 2013, 10:05:06 AM10/3/13
to cuj...@googlegroups.com
Yeah, those are cool, if slightly more complicated ways of doing it.  It does allow the 'core' functionality to remain clean and encourage more reuse later, and I do like how wire really enables/encourages you to keep it that way.  Thanks again for helping me out!
Reply all
Reply to author
Forward
0 new messages