15

Is it possible to reset a resolved jQuery object to an 'unresolved' state and kick off it's initialization and callbacks all over again?

The specific thing I'm doing is that I have a jQuery deferred wrapper over the local file system api. From there I build up higher level deferreds for the things I care about:

var getFs = defFs.requestQuota(PERSISTENT, 1024*1024)
              .pipe (bytes) -> defFs.requestFs(PERSISTENT, bytes)

var getCacheContents = getFs.pipe (fileSystem) -> 
      defFs.getDirectory('Cache', fileSystem.root).pipe (dir) ->
          defFs.readEntries(dir)

Now most of the time, when I call getCacheContents I don't mind the memo-ized values being returned, in fact I prefer it. But, on the occasion when I want to write to the cache I really would like the ability to reset that pipe and have it re-select and rescan the cache next time its accessed.

I could cobble something together from $.Callbacks but a deferred-based solution would really be ideal.

mxcl
  • 26,392
  • 12
  • 99
  • 98
George Mauer
  • 117,483
  • 131
  • 382
  • 612
  • Hmm, I don't think so, not without some hack. Deferreds are resolved or rejected and that's that - any callbacks attached after fire (or don't, depending on whether it's a success/failure callback) immediately. Any reason (sorry if I missed this from your question) why you specifically need the same deferred rather than a new one? – Mitya Aug 06 '12 at 08:58
  • @Utkanos - Other bits of code might already hold a reference to that deferred. (which in my case is just "when you get the cache directory contents") they don't care whether it takes a while to return the results or if they return a value immediately they DO however expect that value to be current and correct – George Mauer Aug 06 '12 at 13:26
  • related: [One time event handling using promises?](http://stackoverflow.com/a/23116562/1048572) – Bergi Jun 04 '14 at 14:26

4 Answers4

19

No. A Promise is by definition a thing that resolves only once - from unresolved to fulfilled OR to rejected. You will not be able to do this with jQuery's Deferreds.

What you are actually searching for are Signals. They are to be fired more than once, but provide a similiar interface. There are some implementations around, you might ceck out js-signals or wire.js.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Upon reviewing those links I don't think that's what I want, wire.js seems like complete overkill for most js projects I've been involved with, and js-signlas seems identical to the built in jQuery.Callbacks. What I want is something with the memoization capabilities and interface of a promise but which I can reset. I suppose I could always include a lazy loading-style lock, it just makes constructing my dsl a little less intention revealing – George Mauer Aug 07 '12 at 13:50
  • While a Promise must be understandably inmutable, why should it not be possible to reset the state on the Deferred object itself? Not trolling, just asking if there's any architectural / pattern reason. – dain Sep 19 '12 at 12:05
  • Promise and Deferred are linked, as soon as the Deferred's state changes the [Promise's] handlers are fired. You can't really reset it and create new promises from it, if that's what you mean. I'd say the architecture pattern is like "the promise is a [save] view on the deferred without the trigger methods" – Bergi Sep 19 '12 at 14:32
  • 1
    Right, you'll have to recreate the Promise handler ties, but since you can only resolve once it doesn't really matter after all as you can't fire twice? I found myself liberally rerequesting Promises whenever I reenter a view and this way if the Deferred has been reset I get the data from the new Promise. What's the motivation for maintaining the original? – dain Sep 19 '12 at 19:43
  • Yes, you can fire it only once. But the benefit of promises is that you can add multiple callbacks (at any time), and they will memorize the [potentially future] value they represent. – Bergi Sep 20 '12 at 10:36
  • jQuery supports signals in the event of custom events (https://learn.jquery.com/events/introduction-to-custom-events/) – pianomansam Aug 02 '23 at 13:44
  • @pianomansam Events are not signals, which have a value at any time instead of "occurring" at specific times. Another term for signals is "observable". – Bergi Aug 02 '23 at 17:26
3

The only solution I could find is to reset the $.Deferred object and return new Promise from that one. It works together with some internal API dirty checking (if something gets edited / deleted), but would be more performant to just reset the existing $.Deferred and let it re-resolve on the next Promise request.

dain
  • 6,475
  • 1
  • 38
  • 47
0

An example of a possible solution is:

$.myDeferredList = [];
$.createRestorableDeferred = function(a,b) {
    // JUST BY SIMPLE $.when().then();
    $.myDeferredList[a] = {
        deferred: $.Deferred()
        , then: b
        ,restore : function() {
            $.myDeferredList['myReady'].deferred = $.Deferred();
            $.when($.myDeferredList['myReady'].deferred).then($.myDeferredList['myReady'].then);
        }
        ,resolve : function() {
            $.myDeferredList['myReady'].deferred.resolve();
        }
    }
    $.when($.myDeferredList['myReady'].deferred).then($.myDeferredList['myReady'].then);
    window[a] = $.myDeferredList['myReady'];
}

 var counter = 0;
 $.createRestorableDeferred('myReady', function () {
     console.log('>> myReady WHEN called',++counter);
     $.myDeferredList['myReady'].restore();

 });

// RESOLVING ways

$.myDeferredList['myReady'].deferred.resolve();
$.myDeferredList.myReady.deferred.resolve();
myReady.resolve();

Results in console:

 >> myReady WHEN called 1

 >> myReady WHEN called 2

 >> myReady WHEN called 3
Santo
  • 71
  • 3
  • Sure but first, I don't think there's any benefit for having a deferred list, you could simply return the object (also no need to implicitly override things in the global namespace with `window[a]` - can you imagine if someone named a deferred `"location"`?) More importantly, this actually doesn't reset the deferred, it simply creates a container that you can add a new deferred to, but any perviously attached `then` continuations will disappear. – George Mauer Mar 11 '18 at 20:50
0

A jQuery custom event placed on a DOM element such as <body> is much more suited for this than Deferred.

pianomansam
  • 111
  • 3