Skip to content Skip to sidebar Skip to footer

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) {

Solution 1:

functiontimeout(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();
}

Solution 2:

I realize this is 2 years old, but in case someone is looking for the answer...

I think Benjamin was close in that you'll want your timeout to be handled separately, so we'll start with his delay function.

functiondelay(ms){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); }, ms);
    return d.promise();
}

Then, if you wanted to wait before code is executed you can call the method you want delayed as a result of this promise.

functiontimeout(funct, args, time) {
    returndelay(time).then(function(){
        // Execute asynchronous code and return its promise// instead of the delay promise. Using "when" should// ensure it will work for synchronous functions as well.return $.when(funct.apply(null, args));
    });
}

This is usually what I'm trying to do when I go looking for a refresher (why I'm here). However, the question was not about delaying the execution, but throwing an error if it took too long. In that case, this complicates things because you don't want to wait around for the timeout if you don't have to, so you can't just wrap the two promises in a "when". Looks like we need another deferred in the mix. (See Wait for the first of multiple jQuery Deferreds to be resolved?)

functiontimeout(funct, args, time) {
    var d = $.Deferred();

    // Call the potentially async funct and hold onto its promise.var functPromise = $.when(funct.apply(null, args));

    // pass the result of the funct to the master defer
    functPromise.always(function(){
        d.resolve(functPromise)
    });

    // reject the master defer if the timeout completes before// the functPromise resolves it one way or anotherdelay(time).then(function(){
        d.reject('timeout');
    });

    // To make sure the functPromise gets used if it finishes// first, use "then" to return the original functPromise.return d.then(function(result){
        return result;
    });
}

We can streamline this, knowing that in this case the master defer only rejects if the timeout happens first and only resolves if the functPromise resolves first. Because of this, we don't need to pass the functPromise to the master defer resolve, because it's the only thing that could be passed and we're still in scope.

functiontimeout(funct, args, time) {
    var d = $.Deferred();

    // Call the potentially async funct and hold onto its promise.var functPromise = $.when(funct.apply(null, args))
        .always(d.resolve);

    // reject the master defer if the timeout completes before// the functPromise resolves it one way or anotherdelay(time).then(function(){
        d.reject('timeout');
    });

    // To make sure the functPromise gets used if it finishes// first, use "then" to return the original functPromise.return d.then(function(){
        return functPromise;
    });
}

Solution 3:

You should always promsiify at the lowest level possible. Let's start from the basics.

I'll use jQuery promises here, but this should really be done with a stronger library like Bluebird Let's start simple, by creating our delay as:

functiondelay(ms){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); }, ms);
    return d.promise();
}

Note delay doesn't do anything surprising, all our delay function does is cause a delay of ms milliseconds.

Now, for your library, we want to create a version of doSomething that works with promises:

 superImportantLibrary.doSomethingAsync = function(){
     var d = $.Deferred();
     superImportantLibrary.doSomething(function(data){ d.resolve(data); });
     return d.promise();
 };

Note both our delay and doSomethingAsync functions both do just one thing. Now the fun begins.

functiontimeout(promise,ms){
    var timeout = delay(ms); // your timeoutvar d = $.Deferred();
    timeout.then(function(){ d.reject(newError("Timed Out")); });
    promise.then(function(data){ d.resolve(data); });
    return d.promise();
}

timeout(superImportantLibrary.doSomethingAsync(),1000).then(function(data){
     // handle success of call
}, function(err){
     // handle timeout or API failure.
});

Now in Bluebird, this whole code would have been:

superImportantLibrary.doSomethingAsync().timeout(1000).then(function(){
    // complete and did not time out.
});

Post a Comment for "In Javascript, How To Wrap A Promise In Timeout?"