如何使用 jQuery 延迟?

IT技术 javascript jquery jquery-deferred
2021-01-13 11:06:10

jQuery 1.5 带来了新的 Deferred 对象和附加的方法.when.Deferred以及._Deferred.

没用过的.Deferred,我已经标注了出处

这些新方法的可能用途是什么,我们如何将它们拟合到模式中?

我已经阅读了API源代码,所以我知道它的作用。我的问题是我们如何在日常代码中使用这些新功能?

我有一个按顺序调用 AJAX 请求的缓冲区类的简单示例(下一个在上一个完成后开始)。

/* Class: Buffer
 *  methods: append
 *
 *  Constructor: takes a function which will be the task handler to be called
 *
 *  .append appends a task to the buffer. Buffer will only call a task when the 
 *  previous task has finished
 */
var Buffer = function(handler) {
    var tasks = [];
    // empty resolved deferred object
    var deferred = $.when();

    // handle the next object
    function handleNextTask() {
        // if the current deferred task has resolved and there are more tasks
        if (deferred.isResolved() && tasks.length > 0) {
            // grab a task
            var task = tasks.shift();
            // set the deferred to be deferred returned from the handler
            deferred = handler(task);
            // if its not a deferred object then set it to be an empty deferred object
            if (!(deferred && deferred.promise)) {
                deferred = $.when();
            }
            // if we have tasks left then handle the next one when the current one 
            // is done.
            if (tasks.length > 0) {
                deferred.done(handleNextTask);
            }
        }
    }

    // appends a task.
    this.append = function(task) {
        // add to the array
        tasks.push(task);
        // handle the next task
        handleNextTask();
    };
};

我在寻找示威和可能的用途.Deferred.when

看到._Deferred.

链接到jQuery.ajax示例的新来源是作弊。

当我们抽象出一个操作是同步还是异步完成时,我特别感兴趣的是哪些技术可用。

6个回答

我能想到的最佳用例是缓存 AJAX 响应。这是Rebecca Murphey 关于该主题的介绍文章的修改示例

var cache = {};

function getData( val ){

    // return either the cached value or jqXHR object wrapped Promise
    return $.when(
        cache[ val ] || 
        $.ajax('/foo/', {
            data: { value: val },
            dataType: 'json',
            success: function( resp ){
                cache[ val ] = resp;
            }
        })
    );
}

getData('foo').then(function(resp){
    // do something with the response, which may
    // or may not have been retrieved using an
    // XHR request.
});

基本上,如果该值在立即从缓存中返回之前已经被请求过一次。否则,AJAX 请求会获取数据并将其添加到缓存中。$.when/.then不关心任何的这一点; 所有你需要关心的是使用响应,.then()在这两种情况下它都会传递给处理程序。jQuery.when()将非Promise/延迟作为已完成处理,立即执行任何.done().then()链上。

当任务可能或可能不异步操作时,延迟是完美的,并且您想从代码中抽象出该条件。

另一个使用$.whenhelper 的真实示例

$.when($.getJSON('/some/data/'), $.get('template.tpl')).then(function (data, tmpl) {

    $(tmpl) // create a jQuery object out of the template
    .tmpl(data) // compile it
    .appendTo("#target"); // insert it into the DOM

});
如果结果为假值,缓存将不起作用。此外,我不喜欢 getData 根据采用的分支返回 2 种不同类型的事实。
2021-03-19 11:06:10
有关 ajax 缓存的更好实现,请参阅下面的 Julian D. 的回答。
2021-03-19 11:06:10
这是有关此主题的有用视频bigbinary.com/videos/3-using-deferred-in-jquery
2021-04-03 11:06:10
我不明白第一个代码示例是如何工作的:我理解对象没有被缓存的情况,但是如果它cache[ val ]不返回Promise(jquery 文档说参数是发送者返回的数据)意味着成员访问.then会出错...对吗?我错过了什么?
2021-04-05 11:06:10
两个辉煌的例子。我实现了与第二个类似的东西,但是有 4 个 ajax 请求,它执行得很好,除了更加清晰、紧凑、逻辑、可维护等。jQuery.Deferred 是一个真正的好东西。
2021-04-08 11:06:10

这是一个与ehynd 的答案略有不同的 AJAX 缓存实现

正如在FortuneRice 的后续问题中所指出的,如果在其中一个请求返回之前执行了多个相同的请求,则 ehynd 的实现实际上并没有阻止多个相同的请求。那是,

for (var i=0; i<3; i++) {
    getData("xxx");
}

如果之前尚未缓存“xxx”的结果,则很可能会产生 3 个 AJAX 请求。

这可以通过缓存请求的延迟而不是结果来解决:

var cache = {};

function getData( val ){

    // Return a promise from the cache (if available)
    // or create a new one (a jqXHR object) and store it in the cache.
    var promise = cache[val];
    if (!promise) {
        promise = $.ajax('/foo/', {
            data: { value: val },
            dataType: 'json'
        });
        cache[val] = promise;
    }
    return promise;
}

$.when(getData('foo')).then(function(resp){
    // do something with the response, which may
    // or may not have been retreived using an
    // XHR request.
});
我认为这仍然不完美,因为一旦第一次获取,您就永远不会清除/更新缓存。这将使 AJAX 调用不适用于任何更新。
2021-03-31 11:06:10

可以使用延迟来代替互斥锁。这和多个ajax的使用场景本质上是一样的。

互斥锁

var mutex = 2;

setTimeout(function() {
 callback();
}, 800);

setTimeout(function() {
 callback();
}, 500);

function callback() {
 if (--mutex === 0) {
  //run code
 }
}

延期

function timeout(x) {
 var dfd = jQuery.Deferred();
 setTimeout(function() {
  dfd.resolve();
 }, x);
 return dfd.promise();
}

jQuery.when(
timeout(800), timeout(500)).done(function() {
 // run code
});

仅将 Deferred 用作互斥锁时,请注意性能影响 (http://jsperf.com/deferred-vs-mutex/2)。虽然 Deferred 提供的便利和额外的好处是值得的,但在实际(基于用户驱动的事件)使用中,性能影响应该不明显。

我很难找到这个。我在一个包含 setInterval 的函数上使用了它,该函数将返回已解决的Promise并在 div 的宽度超过某个数字时自毁。如果我无法解决我的问题,它是用于故障排除和解决方案,但我对此欣喜若狂。
2021-03-18 11:06:10

这是一个自我推销的答案,但我花了几个月的时间研究这个并在 jQuery Conference San Francisco 2012 上展示了结果。

这是谈话的免费视频:

https://www.youtube.com/watch?v=juRtEEsHI9E

我一直在努力的另一个用途是从多个来源获取数据。在下面的示例中,我正在获取现有应用程序中使用的多个独立 JSON 模式对象,以便在客户端和 REST 服务器之间进行验证。在这种情况下,我不希望浏览器端应用程序在加载所有模式之前开始加载数据。$.when.apply().then() 非常适合这个。感谢 Raynos 提供有关使用 then(fn1, fn2) 监视错误情况的指针。

fetch_sources = function (schema_urls) {
    var fetch_one = function (url) {
            return $.ajax({
                url: url,
                data: {},
                contentType: "application/json; charset=utf-8",
                dataType: "json",
            });
        }
    return $.map(schema_urls, fetch_one);
}

var promises = fetch_sources(data['schemas']);
$.when.apply(null, promises).then(

function () {
    var schemas = $.map(arguments, function (a) {
        return a[0]
    });
    start_application(schemas);
}, function () {
    console.log("FAIL", this, arguments);
});