Javascript 如何使用 jQuery 延迟?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4869609/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 14:31:10  来源:igfitidea点击:

How can jQuery deferred be used?

javascriptjqueryjquery-deferred

提问by Raynos

jQuery 1.5 brings the new Deferred object and the attached methods .when, .Deferredand ._Deferred.

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

For those who haven't used .Deferredbefore, I've annotated the source for it.

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

What are the possible usages of these new methods, how do we go about fitting them into patterns?

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

I have already read the APIand the source, so I know what it does. My question is how can we use these new features in everyday code?

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

I have a simple exampleof a buffer class that calls AJAX request in order. (Next one start after previous one finishes).

我有一个按顺序调用 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();
    };
};

I'm looking for demonstrations and possible uses of .Deferredand .when.

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

It would also be lovely to see examples of ._Deferred.

看到._Deferred.

Linking to the new jQuery.ajaxsource for examples is cheating.

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

I am particularly interested in what techniques are available when we abstract away whether an operation is synchronously or asynchronously done.

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

采纳答案by ehynds

The best use case I can think of is in caching AJAX responses. Here's a modified example from Rebecca Murphey's intro post on the topic:

我能想到的最佳用例是缓存 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.
});

Basically, if the value has already been requested once before it's returned immediately from the cache. Otherwise, an AJAX request fetches the data and adds it to the cache. The $.when/.thendoesn't care about any of this; all you need to be concerned about is using the response, which is passed to the .then()handler in both cases. jQuery.when()handles a non-Promise/Deferred as a Completed one, immediately executing any .done()or .then()on the chain.

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

Deferreds are perfect for when the task may or may not operate asynchronously, and you want to abstract that condition out of the code.

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

Another real world example using the $.whenhelper:

另一个使用$.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

});

回答by Julian D.

Here is a slightly different implementation of an AJAX cache as in ehynd's answer.

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

As noted in fortuneRice's follow-up question, ehynd's implementation didn't actually prevent multiple identical requests if the requests were performed before one of them had returned. That is,

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

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

will most likely result in 3 AJAX requests if the result for "xxx" has not already been cached before.

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

This can be solved by caching the request's Deferreds instead of the result:

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

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

回答by user406905

A deferred can be used in place of a mutex. This is essentially the same as the multiple ajax usage scenarios.

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

MUTEX

互斥锁

var mutex = 2;

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

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

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

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

When using a Deferred as a mutex only, watch out for performance impacts (http://jsperf.com/deferred-vs-mutex/2). Though the convenience, as well as additional benefits supplied by a Deferred is well worth it, and in actual (user driven event based) usage the performance impact should not be noticeable.

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

回答by Alex Mcp

This is a self-promotional answer, but I spent a few months researching this and presented the results at jQuery Conference San Francisco 2012.

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

Here is a free video of the talk:

这是谈话的免费视频:

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

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

回答by Elf Sternberg

Another use that I've been putting to good purpose is fetching data from multiple sources. In the example below, I'm fetching multiple, independent JSON schema objects used in an existing application for validation between a client and a REST server. In this case, I don't want the browser-side application to start loading data before it has all the schemas loaded. $.when.apply().then() is perfect for this. Thank to Raynos for pointers on using then(fn1, fn2) to monitor for error conditions.

我一直在努力的另一个用途是从多个来源获取数据。在下面的示例中,我正在获取现有应用程序中使用的多个独立 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);
});     

回答by Julian D.

Another example using Deferreds to implement a cache for any kind of computation (typically some performance-intensive or long-running tasks):

另一个使用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("|");
};

Here is an example of using this class to perform some (simulated heavy) calculation:

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

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

The same underlying cache could be used to cache Ajax requests:

可以使用相同的底层缓存来缓存 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);
});

You can play with the above code in this jsFiddle.

您可以在此 jsFiddle 中使用上述代码。

回答by Kernel James

1) Use it to ensure an ordered execution of callbacks:

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

2) Use it to verify the status of the app:

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

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

jQuery.when(loggedIn, databaseReady).then(function() {
  //do something
});

回答by Milo? Ra?i?

You can use a deferred object to make a fluid design that works well in webkit browsers. Webkit browsers will fire resize event for each pixel the window is resized, unlike FF and IE which fire the event only once for each resize. As a result, you have no control over the order in which the functions bound to your window resize event will execute. Something like this solves the problem:

您可以使用延迟对象来制作在 webkit 浏览器中运行良好的流畅设计。Webkit 浏览器将为窗口调整大小的每个像素触发 resize 事件,这与 FF 和 IE 不同,它们每次调整大小仅触发一次事件。因此,您无法控制绑定到窗口大小调整事件的函数的执行顺序。这样的事情解决了这个问题:

var resizeQueue = new $.Deferred(); //new is optional but it sure is descriptive
resizeQueue.resolve();

function resizeAlgorithm() {
//some resize code here
}

$(window).resize(function() {
    resizeQueue.done(resizeAlgorithm);
});

This will serialize the execution of your code so that it executes as you intended it to. Beware of pitfalls when passing object methods as callbacks to a deferred. Once such method is executed as a callback to deferred, the 'this' reference will be overwritten with reference to the deferred object and will no longer refer to the object the method belongs to.

这将序列化您的代码的执行,以便它按照您的预期执行。将对象方法作为回调传递给 deferred 时要小心陷阱。一旦将此类方法作为对延迟的回调执行,“this”引用将被延迟对象的引用覆盖,并且不再引用该方法所属的对象。

回答by Diego

You can also integrate it with any 3rd-party libraries which makes use of JQuery.

您还可以将它与任何使用 JQuery 的第三方库集成。

One such library is Backbone, which is actually going to support Deferred in their next version.

一个这样的库是 Backbone,它实际上将在下一个版本中支持 Deferred。

回答by jcubic

I've just used Deferred in real code. In project jQuery TerminalI have function exec that call commands defined by user (like he was entering it and pressing enter), I've added Deferreds to the API and call exec with arrays. like this:

我刚刚在实际代码中使用了 Deferred。在jQuery Terminal项目中,我有函数 exec 调用用户定义的命令(就像他输入它并按 Enter 键一样),我已将 Deferreds 添加到 API 并使用数组调用 exec。像这样:

terminal.exec('command').then(function() {
   terminal.echo('command finished');
});

or

或者

terminal.exec(['command 1', 'command 2', 'command 3']).then(function() {
   terminal.echo('all commands finished');
});

the commands can run async code, and exec need to call user code in order. My first api use pair of pause/resume calls and in new API I call those automatic when user return promise. So user code can just use

命令可以运行异步代码,exec 需要按顺序调用用户代码。我的第一个 api 使用了一对暂停/恢复调用,在新的 API 中,当用户返回承诺时,我会自动调用这些调用。所以用户代码可以使用

return $.get('/some/url');

or

或者

var d = new $.Deferred();
setTimeout(function() {
    d.resolve("Hello Deferred"); // resolve value will be echoed
}, 500);
return d.promise();

I use code like this:

我使用这样的代码:

exec: function(command, silent, deferred) {
    var d;
    if ($.isArray(command)) {
        return $.when.apply($, $.map(command, function(command) {
            return self.exec(command, silent);
        }));
    }
    // both commands executed here (resume will call Term::exec)
    if (paused) {
        // delay command multiple time
        d = deferred || new $.Deferred();
        dalyed_commands.push([command, silent, d]);
        return d.promise();
    } else {
        // commands may return promise from user code
        // it will resolve exec promise when user promise
        // is resolved
        var ret = commands(command, silent, true, deferred);
        if (!ret) {
            if (deferred) {
                deferred.resolve(self);
                return deferred.promise();
            } else {
                d = new $.Deferred();
                ret = d.promise();
                ret.resolve();
            }
        }
        return ret;
    }
},

dalyed_commands is used in resume function that call exec again with all dalyed_commands.

dalyed_commands 用于恢复函数,该函数使用所有 dalyed_commands 再次调用 exec。

and part of the commands function (I've stripped not related parts)

和命令功能的一部分(我已经剥离了不相关的部分)

function commands(command, silent, exec, deferred) {

    var position = lines.length-1;
    // Call user interpreter function
    var result = interpreter.interpreter(command, self);
    // user code can return a promise
    if (result != undefined) {
        // new API - auto pause/resume when using promises
        self.pause();
        return $.when(result).then(function(result) {
            // don't echo result if user echo something
            if (result && position === lines.length-1) {
                display_object(result);
            }
            // resolve promise from exec. This will fire
            // code if used terminal::exec('command').then
            if (deferred) {
                deferred.resolve();
            }
            self.resume();
        });
    }
    // this is old API
    // if command call pause - wait until resume
    if (paused) {
        self.bind('resume.command', function() {
            // exec with resume/pause in user code
            if (deferred) {
                deferred.resolve();
            }
            self.unbind('resume.command');
        });
    } else {
        // this should not happen
        if (deferred) {
            deferred.resolve();
        }
    }
}