在JavaScript中,如何在超时中包装一个promise?

问题描述 投票:17回答:3

这是使用deferred / 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();
}

现在我们可以执行一些名为myFunc的异步函数并处理超时:

// 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' );
    }
);

好吧,让我们来讲一下这个故事吧!想象一下,myFunc本身会返回一个承诺(注意:承诺不推迟,我不能改变它):

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

现在,如果我在qazxsw poi中包装qazxsw poi,我将失去处理错误然后超时的能力。如果myFunc发出进展事件,我也会放弃它。

所以问题是:如何修改timeout函数,以便它可以接受函数返回promises而不会丢失它们的错误/进度信息?

javascript jquery promise jquery-deferred deferred
3个回答
8
投票
myFunc

3
投票

你应该始终在尽可能最低的水平上进行宣传。让我们从基础开始。

我将在这里使用jQuery promises,但这应该通过更强大的库来完成,比如Bluebird让我们开始简单,通过创建我们的timeout

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

注意延迟没有做任何令人惊讶的事情,我们所有的延迟功能都会导致delay毫秒的延迟。

现在,对于您的库,我们想要创建一个与promises一起使用的function delay(ms){ var d = $.Deferred(); setTimeout(function(){ d.resolve(); }, ms); return d.promise(); } 版本:

ms

请注意我们的延迟和doSomething函数都只做一件事。现在开始变得有趣了。

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

现在在Bluebird中,整个代码将是:

doSomethingAsync

3
投票

我意识到这是2岁,但万一有人正在寻找答案......

我认为Benjamin很接近你会希望你的超时分开处理,所以我们将从他的延迟功能开始。

function timeout(promise,ms){
    var timeout = delay(ms); // your timeout
    var d = $.Deferred();
    timeout.then(function(){ d.reject(new Error("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.
});

然后,如果您想在执行代码之前等待,则可以调用您希望因此承诺而延迟的方法。

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

这通常是我在寻找复习时想要做的事情(为什么我在这里)。然而,问题不在于延迟执行,而是在花费太长时间时抛出错误。在这种情况下,这会使事情变得复杂,因为如果你不需要,你不想等待超时,所以你不能将两个promises包装在“when”中。看起来我们需要在混合中推迟另一个。 (见function delay(ms){ var d = $.Deferred(); setTimeout(function(){ d.resolve(); }, ms); return d.promise(); }

function timeout(funct, args, time) {
    return delay(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));
    });
}

我们可以简化这一点,因为在这种情况下,主延迟只会在超时发生时拒绝,并且只有在functPromise首先解析时才会解析。因此,我们不需要将functPromise传递给主延迟解决方案,因为它是唯一可以传递的东西,我们仍然在范围内。

Wait for the first of multiple jQuery Deferreds to be resolved?
© www.soinside.com 2019 - 2024. All rights reserved.