How do I check if a promise or deferred object is completed?

222 views
Skip to first unread message

Jacob

unread,
Mar 24, 2015, 12:29:27 PM3/24/15
to def...@googlegroups.com
How do I check if a promise or deferred object is completed? It looks like the state management is done by the resolver which is hidden via closure. Am I missing another way to access the state?

Brian Kotek

unread,
Mar 24, 2015, 12:57:04 PM3/24/15
to deftjs
You should just be able to use then() to attach a callback that will trigger when the Promise is fulfilled (or rejected). If the Promise is already fulfilled, the success callback should just trigger immediately. This is part of the point of Promises: nothing using the Promise knows or cares about the state. Everything is done through the callbacks, so that you interact with Promises the same way, regardless of whether they're fulfilled yet or not.

Make sense?

On Tue, Mar 24, 2015 at 12:29 PM, Jacob <ja...@fareclock.com> wrote:
How do I check if a promise or deferred object is completed? It looks like the state management is done by the resolver which is hidden via closure. Am I missing another way to access the state?

--
Deft JS home page: http://deftjs.org/
Wiki: https://github.com/deftjs/DeftJS/wiki
For rules and tips on asking questions on the Deft JS mailing list: https://github.com/deftjs/DeftJS/wiki/Asking-Questions-on-the-Mailing-List
---
You received this message because you are subscribed to the Google Groups "Deft JS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to deftjs+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/deftjs/bdbc4f8f-c747-4d1b-bc63-65234190b88d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jacob

unread,
Mar 24, 2015, 1:21:07 PM3/24/15
to def...@googlegroups.com
Thanks Brian. I get that, and it's always worked for me. I have a particular scenario now where the path of least resistance seems to me to be able to check the promise state. I'm not asking you to solve my problem, but I thought I'd paste my code to illustrate the need. The issue comes up in the whenIdleForSeconds and idleTimeout methods, where I set a custom canceled property on the deferred object:


//
// IdleMonitor is responsible for monitoring the system for non-idle activity and providing API
// hooks for clients to observe idleness.
//
Ext.define('Fc.tc.service.IdleMonitor', {


    config
: {
        documentEvents
: ['touchstart', 'touchmove', 'touchend', 'keydown', 'keyup']
   
},


    constructor
: function (config) {


       
var me = this;


        me
.lastActivity = null;


        me
.initConfig(config);


       
// defer start, so we don't hold up ui
       
Ext.defer(me.start, 100, me);
   
},


    start
: function () {


       
var me = this,
            onActivity
= Ext.bind(me.onActivity, me);


       
// listen to activity events
       
Ext.each(me.getDocumentEvents(), function (event) {
            document
.addEventListener(event, onActivity, false);
       
});


   
},


    onActivity
: function () {


       
this.lastActivity = new Date();
   
},


    idleSince
: function () {


       
return Ext.Date.clone(this.lastActivity); // pass by value, not by reference
   
},


    startIdleTimer
: function (args, millis) {


        window
.setTimeout(Ext.bind(me.idleTimeout, me, args), millis);
   
},


    idleTimeout
: function (deferred, millis) {


       
var me = this;


       
if(deferred.canceled) {
           
// ignore
           
return;
       
}


       
if (me.lastActivity < Ext.Date.add(new Date(), Ext.Date.MILLI, -millis)) {
            deferred
.resolve();
       
}
       
else {
           
// restart timer, adjust delay for next possible idle state
            me
.startIdleTimer(
               
[deferred, millis],
               
Ext.diff(me.lastActivity, new Date(), Ext.Date.MILLIS) + millis
           
);
       
}
   
},


    whenIdleForSeconds
: function (seconds) {


       
var deferred = Ext.create('Deft.Deferred'),
            millis
= seconds*1000;


        me
.startIdleTimer([deferred, millis], millis);


       
return deferred.promise.then({
            failure
: function () {
               
// client canceled promise, so signal to idleTimeout not to restart timer
                deferred
.canceled = true;
           
}
       
});
   
}
});

Brian Kotek

unread,
Mar 24, 2015, 1:52:49 PM3/24/15
to deftjs
OK...but just wondering where the need to check the state of the Promise comes into play?

Brian Kotek

unread,
Mar 24, 2015, 1:57:22 PM3/24/15
to deftjs
If you mean the custom cancelled property you're checking, can you just use then() to attach a failure callback to check if it failed? Or is the problem that you want to treat idleTimeout() as a synchronous method?

Jacob

unread,
Mar 24, 2015, 1:58:06 PM3/24/15
to def...@googlegroups.com
The idle timer needs to check the promise state each time to determine whether to keep restarting. Basically, the reason it checks the promise state is because the caller may have canceled the promise. I could a wrapper around the deferred object I pass to the timer in order to track the cancel state, but I figured that checking state directly on the deferred object would be simpler.

Jacob

unread,
Mar 24, 2015, 2:01:25 PM3/24/15
to def...@googlegroups.com
Yes, the reason is so that idleTimer knows if the promise was canceled. I don't see any other way of doing it other than passing an additional state object to idleTimeout that gets updated when the promise is canceled. But that's basically the same thing as exposing the state directly within the promise.

Brian Kotek

unread,
Mar 24, 2015, 2:09:40 PM3/24/15
to deftjs
Yeah I think I follow, and the complication looks to be that you're trying to do something synchronous (checking idleTimeout()) in the middle of this async process (whenIdleForSeconds()). 

The Deft Promise API basically implements the CommonJS Promises/A spec (which may now have become Promises/A+, since the Promises/A site looks to be down). So in this case, passing along additional info in your call to idleTimeout() probably makes the most sense? 

Jacob

unread,
Mar 24, 2015, 2:09:51 PM3/24/15
to def...@googlegroups.com
Ok, I changed the state management to track promise state separately outside the Deft objects. It maintains your API contract, but the pattern is basically the same:


//
// IdleMonitor is responsible for monitoring the system for non-idle activity and providing API
// hooks for clients to observe idleness. Unlike the usual way of services not being available
// globally, the IdleMonitor is injected via Deft because it needs to expose methods to clients.

//
Ext.define('Fc.tc.service.IdleMonitor', {


    config
: {
        documentEvents
: ['touchstart', 'touchmove', 'touchend', 'keydown', 'keyup']
   
},


    constructor
: function (config) {


       
var me = this;


        me
.lastActivity = null;


        me
.initConfig(config);


       
// defer start, so we don't hold up ui
       
Ext.defer(me.start, 100, me);
   
},



   
// @private

    start
: function () {


       
var me = this,
            onActivity
= Ext.bind(me.onActivity, me);


       
// listen to activity events
       
Ext.each(me.getDocumentEvents(), function (event) {
            document
.addEventListener(event, onActivity, false);
       
});


   
},



   
// @private

    onActivity
: function () {


       
this.lastActivity = new Date();
   
},


    idleSince
: function () {


       
return Ext.Date.clone(this.lastActivity); // pass by value, not by reference
   
},



   
// @private
    startIdleTimer
: function (options, millis) {


        window
.setTimeout(Ext.bind(me.idleTimeout, me, [options]), millis);
   
},


   
// @private
    idleTimeout
: function (options, millis) {


       
var me = this;


       
if(options.canceled) {
           
// ignore
           
return;
       
}


       
if (me.lastActivity < Ext.Date.add(new Date(), Ext.Date.MILLI, -options.millis)) {
            options
.deferred.resolve();
       
}
       
else {
           
// There was activity during the waiting period, so restart timer,
           
// while adjusting delay for next possible idle state.
            me
.startIdleTimer(
                options
,
               
Ext.diff(me.lastActivity, new Date(), Ext.Date.MILLI) + options.millis
           
);

       
}
   
},


    whenIdleForSeconds
: function (seconds) {


       
var deferred = Ext.create('Deft.Deferred'),

            millis
= seconds*1000,
            options
= {
                deferred
: deferred,
                millis
: millis,
                canceled
: false
           
};


        me
.startIdleTimer(options, millis);



       
return deferred.promise.then({
            failure
: function () {
               
// client canceled promise, so signal to idleTimeout not to restart timer

                options
.canceled = true;
           
}
       
});
   
}
});

Jacob

unread,
Mar 24, 2015, 2:12:33 PM3/24/15
to def...@googlegroups.com
Thanks a lot Brian for your input. I think it will work out fine the way I pasted in my last message.
Reply all
Reply to author
Forward
0 new messages