Wilsonhut

Deal with it or don't

jQuery Deferred-ified window.setTimeout

EDIT: $.defer was renamed to $.whereas

[Code] [jsfiddle sample][on github]

“Too bad you can’t use window.setTimout and have it implement the jQuery Deferred interface.”

We’re all upset about that. So…

“Wait. What? Now you can?!?”

Right. Now you can. I searched the internet and couldn’t find an existing one, to my surprise. I wrote it for you though.

You can skip right to the Code below, skip to a jsfiddle sample, or keep reading for more explanation.

It’s almost as simple as my sample code in my last post explaining the basics of Deferred with the addition of an actual setTimeout… Make a $.Deferred. Call the provided func in a setTimeout, using the provided delay and parameters. After the call to the provided func, call .resolve, and in a catch block, call .reject.

“How do you use it?”

Use it just like setTimeout
setTimeout:

setTimeout(function(){
    console.log("I'm in the future. " + new Date());
}, 2000);

My new $.defer method:

$.defer(function(){
    console.log("I'm in the future. " + new Date());
}, 2000);

It’s the same. You can even pass parameters, just like setTimout. (I didn’t bother implementing the overload that takes a string and eval’s it, because who cares)

“So what’s the big deal?”

  • It works the same way as your beloved setTimeout, only with the $.defer, you can chain callbacks on the end…
    $.defer(function(){
        console.log("I'm in the future. " + new Date());
    }, 2000)
    .done(function(){
        console.log("All done");
    });
    

    and you get all the benefits of the rest of the callbacks – done/fail/then/always, etc.

  • You can use it anywhere that expects a promise – for example, you can use it as a participant in jQuery’s $.when()!
  • Since there are callbacks, you have a way to get the return value of the function that you called.
    $.defer(function(){
        return "no you didn't";
    }, 2000)
    .done(function(x){
        console.log(x); // x is "no you didn't" 
    });
    
  • It’s cancel-able.
    $.defer(function(){
       console.log("Doing work...");
    }, 2000)
    .progress(function(){
        console.log("Sorry to have to cancel this.");
        this.cancel();
    })
    .done(function(){
        console.log("Won't get here.");
    })
    .fail(function(){
        console.log("This will happen");
    });
  • You can use it without the function parameter to return a promise that will resolve in [delay] milliseconds

    return $.defer(100);
    

    The delay is optional too. The default delay of 0 (zero) will be used.

    return $.defer(function(){alert("ASYNC!");});
    

    or

    return $.defer();
    

“You had me at ‘Promise’, now shut up and give me the code”

Here it is. It’s a little longer than the way I explained it above, but that’s just because it has the code for cancelling, and I like for the actually code to look long and complex to make me look smarter.


THE CODE


;(function($) {
	var slice = Array.prototype.slice;
	$.defer = function( /*[func] [, delay] [, funcParameters]*/) {
		var args = arguments,
		    isFunc = $.isFunction(args[0]),
		    func = isFunc ? args[0] : $.noop,
		    delay = (isFunc ? args[1] : args[0]) || 0,
		    funcArgs = isFunc ? slice.call(args, 2) : [],
		    isCancelled = false,
		    cancel = function(reject /* = true */) {
			    clearTimeout(timerId);
			    isCancelled = true;
			    if ((!arguments.length || reject) && deferred.state() === "pending") {
				    deferred.rejectWith(null, funcArgs);
			    }
		    },
		    deferred = $.Deferred(),
		    timerId = setTimeout(function() {
			    deferred.notifyWith(promise, funcArgs);
			    if (isCancelled) {
				    return;
			    }
			    try {
				    var result = func.apply(this, funcArgs);
				    deferred.resolveWith(result, funcArgs);
			    } catch(e) {
				    deferred.rejectWith(e, funcArgs);
			    }
		    }, delay),
		    promise = $.extend(deferred.promise(), {
			    cancel: cancel
		    });
		return promise;
	};
})(jQuery);

10 responses to “jQuery Deferred-ified window.setTimeout

  1. akhikhl June 1, 2013 at 6:37 am

    Excellent replacement for setTimeout.
    Probably, it would be better to write “$.fn.defer = function …” (note “fn”)?
    jQuery.fn === jQuery.prototype

  2. Pingback: Introducing… jQuery Gasp – Give a little breathing room to your browser-intensive jQuery | Wilsonhut

  3. Pingback: $.defer for advanced promise users – progress, only when you need it! | Wilsonhut

Leave a comment