mnowotka mnowotka - 2 months ago 11
Javascript Question

in JavaScript, how to wrap a promise in timeout?

It's a common pattern to implement timeout of some asynchronous function, using deffered/promise:

// Create a Deferred and return its Promise
function timeout(funct, args, time) {
var dfd = new jQuery.Deferred();

// execute asynchronous code
funct.apply(null, args);

// When the asynchronous code is completed, resolve the Deferred:
dfd.resolve('success');

setTimeout(function() {
dfd.reject('sorry');
}, time);
return dfd.promise();
}


Now we can execute some asynchronous function called
myFunc
and handle timeout:

// Attach a done and fail handler for the asyncEvent
$.when( timeout(myFunc, [some_args], 1000) ).then(
function(status) {
alert( status + ', things are going well' );
},
function(status) {
alert( status + ', you fail this time' );
}
);


OK, let's make a twist in this story! Imagine that the
myFunc
itself returns a promise (NOTE: promise NOT deferred and I can't change it):

function myFunc(){
var dfd = new jQuery.Deffered();
superImportantLibrary.doSomething(function(data)){
if(data.length < 5){
dfd.reject('too few data');
}
else{
dfd.resolve('success!');
}
}, {'error_callback': function(){
dfd.reject("there was something wrong but it wasn't timeout");}
}});
return dfd.promise();
}


Now if I wrap
myFunc
in
timeout
, I will loose the ability to handle errors different then timeout. If
myFunc
emit progress events, I will loose this as well.

So the question is: how to modify
timeout
function so it can accept functions returning promises without loosing their errors/progress information?

Answer
function timeout(funct, args, time) {
    var deferred = new jQuery.Deferred(),
        promise = funct.apply(null, args);

    if (promise) {
        $.when(promise)
            .done(deferred.resolve)
            .fail(deferred.reject)
            .progress(deferred.notify);
    }

    setTimeout(function() {
        deferred.reject();
    }, time);

    return deferred.promise();
}
Comments