如何使用jQuery延迟?

Ray*_*nos 278 javascript jquery jquery-deferred

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();
    };
};
Run Code Online (Sandbox Code Playgroud)

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

看到例子也很可爱._Deferred.

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

赏金:向我们展示当我们抽象出操作是同步还是异步完成时可用的技术.

ehy*_*nds 212

我能想到的最好的用例是缓存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.
});
Run Code Online (Sandbox Code Playgroud)

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

当任务可能异步或不异步运行时,延迟是完美的,并且您希望从代码中抽象出该条件.

另一个使用$.when帮助器的真实示例:

$.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

});
Run Code Online (Sandbox Code Playgroud)

  • 以下是关于此主题的有用视频http://www.bigbinary.com/videos/3-using-deferred-in-jquery (5认同)
  • 如果结果是伪值,则缓存将不起作用.另外,我不喜欢getData返回2种不同类型的事实,具体取决于所采用的分支. (5认同)
  • 两个宝石的例子.我实现了类似于第二个的东西,但有4个ajax请求,并且它表现良好,除了更易读,紧凑,逻辑,可维护等等.jQuery.Deferred是一个真正的好东西. (4认同)
  • 请参阅下面的Julian D.的答案,以更好地实现ajax缓存. (3认同)

Jul*_* D. 79

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

正如fortuneRice的后续问题所述,如果请求在其中一个请求返回之前执行,则ehynd的实现实际上并未阻止多个相同的请求.那是,

for (var i=0; i<3; i++) {
    getData("xxx");
}
Run Code Online (Sandbox Code Playgroud)

如果之前尚未缓存"xxx"的结果,则很可能会导致3个AJAX请求.

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

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.
});
Run Code Online (Sandbox Code Playgroud)


use*_*905 45

可以使用延迟代替互斥锁.这与多个ajax使用场景基本相同.

MUTEX

var mutex = 2;

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

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

function callback() {
 if (--mutex === 0) {
  //run code
 }
}
Run Code Online (Sandbox Code Playgroud)

DEFERRED

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
});
Run Code Online (Sandbox Code Playgroud)

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


Ale*_*Mcp 28

这是一个自我宣传的答案,但我花了几个月的时间研究这个并在2012年旧金山jQuery大会上展示了结果.

这是一个免费的谈话视频:

http://www.confreaks.com/videos/993-jqcon2012-i-promise-to-show-you-when-to-use-deferreds


Elf*_*erg 20

我一直善用的另一个用途是从多个来源获取数据.在下面的示例中,我将获取现有应用程序中使用的多个独立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);
});     
Run Code Online (Sandbox Code Playgroud)


Jul*_* D. 10

另一个使用Deferreds为任何类型的计算实现缓存的示例(通常是一些性能密集型或长时间运行的任务):

var ResultsCache = function(computationFunction, cacheKeyGenerator) {
    this._cache = {};
    this._computationFunction = computationFunction;
    if (cacheKeyGenerator)
        this._cacheKeyGenerator = cacheKeyGenerator;
};

ResultsCache.prototype.compute = function() {
    // try to retrieve computation from cache
    var cacheKey = this._cacheKeyGenerator.apply(this, arguments);
    var promise = this._cache[cacheKey];

    // if not yet cached: start computation and store promise in cache 
    if (!promise) {
        var deferred = $.Deferred();
        promise = deferred.promise();
        this._cache[cacheKey] = promise;

        // perform the computation
        var args = Array.prototype.slice.call(arguments);
        args.push(deferred.resolve);
        this._computationFunction.apply(null, args);
    }

    return promise;
};

// Default cache key generator (works with Booleans, Strings, Numbers and Dates)
// You will need to create your own key generator if you work with Arrays etc.
ResultsCache.prototype._cacheKeyGenerator = function(args) {
    return Array.prototype.slice.call(arguments).join("|");
};
Run Code Online (Sandbox Code Playgroud)

以下是使用此类执行某些(模拟重)计算的示例:

// The addingMachine will add two numbers
var addingMachine = new ResultsCache(function(a, b, resultHandler) {
    console.log("Performing computation: adding " + a + " and " + b);
    // simulate rather long calculation time by using a 1s timeout
    setTimeout(function() {
        var result = a + b;
        resultHandler(result);
    }, 1000);
});

addingMachine.compute(2, 4).then(function(result) {
    console.log("result: " + result);
});

addingMachine.compute(1, 1).then(function(result) {
    console.log("result: " + result);
});

// cached result will be used
addingMachine.compute(2, 4).then(function(result) {
    console.log("result: " + result);
});
Run Code Online (Sandbox Code Playgroud)

可以使用相同的底层缓存来缓存Ajax请求:

var ajaxCache = new ResultsCache(function(id, resultHandler) {
    console.log("Performing Ajax request for id '" + id + "'");
    $.getJSON('http://jsfiddle.net/echo/jsonp/?callback=?', {value: id}, function(data) {
        resultHandler(data.value);
    });
});

ajaxCache.compute("anID").then(function(result) {
    console.log("result: " + result);
});

ajaxCache.compute("anotherID").then(function(result) {
    console.log("result: " + result);
});

// cached result will be used
ajaxCache.compute("anID").then(function(result) {
    console.log("result: " + result);
});
Run Code Online (Sandbox Code Playgroud)

你可以在这个jsFiddle中使用上面的代码.


Ker*_*mes 9

1)使用它来确保有序执行回调:

var step1 = new Deferred();
var step2 = new Deferred().done(function() { return step1 });
var step3 = new Deferred().done(function() { return step2 });

step1.done(function() { alert("Step 1") });
step2.done(function() { alert("Step 2") });
step3.done(function() { alert("All done") });
//now the 3 alerts will also be fired in order of 1,2,3
//no matter which Deferred gets resolved first.

step2.resolve();
step3.resolve();
step1.resolve();
Run Code Online (Sandbox Code Playgroud)

2)用它来验证应用程序的状态:

var loggedIn = logUserInNow(); //deferred
var databaseReady = openDatabaseNow(); //deferred

jQuery.when(loggedIn, databaseReady).then(function() {
  //do something
});
Run Code Online (Sandbox Code Playgroud)