Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jquery/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在JavaScript中,如何在超时中包装承诺?_Javascript_Jquery_Promise_Jquery Deferred_Deferred - Fatal编程技术网

在JavaScript中,如何在超时中包装承诺?

在JavaScript中,如何在超时中包装承诺?,javascript,jquery,promise,jquery-deferred,deferred,Javascript,Jquery,Promise,Jquery Deferred,Deferred,使用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

使用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();
}
现在我们可以执行一些名为
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
本身返回一个承诺(注意:承诺不延迟,我不能更改):

函数myFunc(){
var dfd=newjquery.Deffered();
superImportantLibrary.doSomething(函数(数据)){
如果(数据长度<5){
拒绝(数据太少);
}
否则{
dfd.决心(“成功!”);
}
},{'error_callback':函数(){
拒绝(“出现了问题,但不是超时”);}
}});
返回dfd.promise();
}
现在,如果我将
myFunc
包装在
timeout
中,我将失去处理与超时不同的错误的能力。如果
myFunc
发出进度事件,我也会释放它


所以问题是:如何修改
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();
}
我将在这里使用jQuery Promissions,但这确实应该通过一个更强大的库(如Bluebird)来实现。让我们从简单开始,创建我们的
delay
,如下所示:

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

请注意,我们的delay和doSomethingAsync函数都只做一件事。现在乐趣开始了

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.
});
现在在Bluebird中,整个代码应该是:

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

我知道这是2岁,但万一有人在寻找答案

我认为Benjamin很接近于你希望你的超时被单独处理,所以我们从他的延迟函数开始

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传递给master defer resolve,因为它是唯一可以传递的东西,并且我们仍然在范围内

function timeout(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 another
    delay(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;
    });
}

不。您不能对返回承诺的函数调用
reject
resolve
。很抱歉,我不能将
myFunc
更改为返回deferred而不是promise…我认为如果(deferred.state()==='pending')延迟,您可以跳过
,因为
deferred.reject()
如果延迟已被拒绝或解决,则不会执行任何操作。@JoeBrockhaus-嗯,审阅者只是错了(这种情况时有发生)。删除
if(deferred.state()===“pending”)
只会删除死代码,不会以任何方式更改功能,因为
deferred.reject()
在内部已经有自己的检查,如果状态不是
pending
,则不会执行任何操作(根据承诺规范)。哦,好的。谢谢大家,我已经修好了。调用
$.Deferred()
时是否需要
新建
?我没有这么做。您的原语是错误的,您需要分两个阶段进行承诺,首先-承诺superImportantLibrary.doSomething方法,然后执行承诺返回。另外,请避免jQuery承诺,它们与其他实现相比是可怕的。@BenjaminGruenbaum-哪种实现?为什么jQuery承诺很可怕?你说“你的原语错了”是什么意思?我怎样才能“promisify”
superImportantLibrary.doSomething
如果它是库而不是我自己的代码,你能写一些示例代码来解释你的意思吗?我担心我不需要证明自己就无法做出这样的声明:)所以(转换库本身),至于库,我会使用@BenjaminGruenbaum-so
myFunc
是一种展示
superImportantLibrary.doSomething
方法的方法。它只会回报一个承诺。你为什么说这是错的?我仍然希望有一些代码能够解释如何以正确的方式执行此操作。谢谢其他链接!我已经添加了一个答案,如果您对任何事情不确定,请随时要求澄清。是的,但您也可以编写
timeout(myFunc(),1000)。然后(…)
,所以我真的不明白您为什么坚持用
doSomethingAsync
替换它。除此之外,在
promise.then(…)
部分中,应该有处理错误的代码,也应该有处理进度的代码,但我知道您将其作为实现细节。如果
promise
被拒绝怎么办?进度通知呢?我感觉到一个延迟的反模式:-)最干净的解决方案可能类似于
Promise.race(Promise,rejectAfterDelay(ms))
@Bergi是的,最干净的解决方案肯定是我在Bluebird上说的最新解决方案。问题是jQuery没有
.race
方法或任何类似的方法,因此实现它将非常难看。
doSomethingAsync
只是包装库函数,而不是对长度等进行验证检查。记住,承诺是安全的。
function timeout(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 another
    delay(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;
    });
}
function timeout(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 another
    delay(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;
    });
}