延迟jquery承诺
我找不到jQuery
promises的delay
或wait
函数。 我在SO上找到了一个函数( 使用jQuery.Deferred来避免嵌套的setTimeout回调 ):
function delay(time) { return function () { console.log("Delaying"); var ret = new $.Deferred(); setTimeout(function () { ret.resolve(); }, time); return ret; }; }
而且,这是我使用它的方式:
run: function () { return $() .promise() .then(function () { console.log("call together"); console.log("call together"); }) .then(delay(2000)) .then(function () { console.log("call first"); }) .then(delay(2000)) .then(function () { console.log("call second"); }) }
我想扩展我可以编写的promise或deferred对象:
run: function () { return $() .promise() .then(function () { console.log("call together"); console.log("call together"); }) .delay(2000) .then(function () { console.log("call first"); }) .delay(2000) .then(function () { console.log("call second"); }) }
正如@Bergi所说,jQuery Deferreds / Promises不能通过原型inheritance扩展。
相反,jQuery采用的模型是允许使用语法扩展单个Promise实例:
deferred.promise(target); //or, promise.promise(target); //(though the documentation doesn't make this clear) // where `target` is an "object onto which the promise methods have to be attached" // see https://api.jquery.com/deferred.promise/
通过使用一堆方法定义构造函数,可以使用简单的语法扩展任何jQuery Deferred或Promise
.promise(Constructor())
在我未发表的,未记录的jQuery承诺Playground中,构造函数名为$P
并保存在jQuery名称空间中,因此我使用的实际语法是:
.promise($.$P())
您需要注意这一点,在大多数情况下,没有必要显式调用$.$P()
因为Playground包含一个返回已经扩展的Promise的$.when_()
方法。
这是Playground的缩写版本,足以提供.delay()
方法:
(function($) { /* *********************************** * The $.$P function returns an object * designed to be extended with * promise methods using the syntax : * myDeferred.promise($.$P()) * myPromise.promise($.$P()) * where `myDeferred`/`myPromise` * are jQuery Deferred/Promise objects. * ***********************************/ /* *********************************** * Methods * ***********************************/ $.$P = function() { if (this instanceof $.$P) { return this; } else { return new $.$P(); } }; $.$P.prototype.then_ = function(fa, fb) { /* A promise method that is the same as .then() * but makes these extra methods available * down-chain. */ return this.then(fa||null, fb||null).promise($.$P()); } $.$P.prototype.delay_ = function(ms) { /* A promise method that * introduces a down-chain delay. */ var promise = this; function f(method) { return function() { setTimeout(function(){ method.apply(null,this); }.bind(arguments), ms||0); }; } return $.Deferred(function(dfrd) { promise.then(f(dfrd.resolve), f(dfrd.reject)); }).promise($.$P()); } /* *********************************** * Utility functions * ***********************************/ function consolidate(args) { /* Convert mixed promises/arrays_of_promises to single array. * Called by all the when_() methods below. */ return Array.prototype.slice.apply(args).reduce(function(arr, current) { return arr.concat(current); }, []); } /* *********************************** * This section extends the jQuery namespace * with a "jQuery.when_()" method. * *********************************** */ $.extend({ 'when_': function() { return $.when.apply(null, consolidate(arguments)).promise($.$P()).then_(function() { return consolidate(arguments); }); }, }); })(jQuery);
完整的Playground还包含一大堆用于其他目的的静态和promise实例方法,并且开发它们是该剧的本质。
使用Playgound的基本规则如下:
- 所有Playground的静态和promise方法都以“_”下划线结尾。
- 静态方法,例如
$.when_()
,只需安装Playgound即可使用。 - 通过包含静态方法(例如
.when_()
或链接.promise($.$P())
来扩展promise链中的.promise($.$P())
。 - 在承诺链中,通过使用“…_”方法而不是标准方法(例如
.then_()
代替.then_()
,扩展仍然可用(在链中.then()
。
所以这里是如何使用它来强加问题所需的延迟:
jQuery(function($) { var MYNAMESPACE = { run: function (t) { return $.when_() .then_(function () { log("call together"); log("call together"); }) .delay_(t) .then_(function () { log("call first"); }) .delay_(t) .then_(function () { log("call second"); }); } } });
DEMO
在演示中,按钮的单击处理程序进一步指示了如何使用Playground。
关于使用游乐场的Provisos:
- 正如我所说 – 这是一个游乐场 。
- 作为jQuery的适配器,而不是补丁,它在某些地方效率非常低。 最糟糕的是,除了返回的方法之外,某些方法还创建了一个中间承诺。
- 未按照生产代码中使用的标准进行测试,因此请谨慎使用。
最后,如果你决定用jQuery实现延迟,只考虑上面的内容。 使用已经具有.delay()
方法的promise lib要简单得多。
编辑,更新
尝试将属性delay
添加到jQuery.Deferred
delay: function(t) { return this.then(function() { var args = arguments; return new $.Deferred(function(d) { setTimeout(function() { // return `data`, if any, to next method, eg, `.then`, in chain d.resolveWith(this, args) }.bind(this), t || 0) }).promise() }) }
(function($) { $.Deferred = function(a) { var b = [ ["resolve", "done", $.Callbacks("once memory"), "resolved"], ["reject", "fail", $.Callbacks("once memory"), "rejected"], ["notify", "progress", $.Callbacks("memory")] ], c = "pending", d = { delay: function(t) { return this.then(function() { var args = arguments; return new $.Deferred(function(d) { setTimeout(function() { // return `data`, if any, to next method, eg, `.then`, in chain d.resolveWith(this, args) }.bind(this), t || 0) }).promise() }) }, state: function() { return c }, always: function() { return e.done(arguments).fail(arguments), this }, then: function() { var a = arguments; return $.Deferred(function(c) { $.each(b, function(b, f) { var g = $.isFunction(a[b]) && a[b]; e[f[1]](function() { var a = g && g.apply(this, arguments); a && $.isFunction(a.promise) ? a.promise() .done(c.resolve) .fail(c.reject) .progress(c.notify) : c[f[0] + "With"](this === d ? c.promise() : this, g ? [a] : arguments) }) }), a = null }).promise() }, promise: function(a) { return null != a ? $.extend(a, d) : d } }, e = {}; return d.pipe = d.then, $.each(b, function(a, f) { var g = f[2], h = f[3]; d[f[1]] = g.add, h && g.add(function() { c = h }, b[1 ^ a][2].disable, b[2][2].lock), e[f[0]] = function() { return e[f[0] + "With"](this === e ? d : this, arguments), this }, e[f[0] + "With"] = g.fireWith }), d.promise(e), a && a.call(e, e), e } }(jQuery)); var p = { run: function() { return $() .promise() .then(function() { console.log("call together"); console.log("call together"); // do stuff // pass `data` to next `.then` return "call first"; }) .delay(2000) .then(function(data) { console.log(data); }) .delay(2000) .then(function() { console.log("call second"); }) } }; p.run();
这是我的解决方案。 我将$.Deferred(afterBuild)
换$.Deferred(afterBuild)
并包装原始的afterBuild
,然后包装.promise(obj)
方法,使用自定义delay
方法扩展给定的obj
。 其中使用window.setTimeout
:
注意:它只延迟done
分支。
function extendPromises(extensions) { $.Deferred = (function (originalDeferred) { return function (afterBuild) { var newAfterBuild = function (d) { d.promise = (function (originalPromise) { return function (obj) { return originalPromise.call(this, $.extend(obj, extensions)); }; })(d.promise); if (afterBuild) afterBuild.apply(this, arguments); return this; }; return originalDeferred.call(this, newAfterBuild); }; })($.Deferred); } extendPromises({ delay: function (delay) { return this.then(function (value) { var d = $.Deferred(); window.setTimeout(function () { d.resolve(value); }, delay); return d.promise(); }); } }); // so now I can do: $.when("hello") .then(function (value) { $("#log").append(value+"\n"); return value; }) .delay(1000) .then(function (value) { $("#log").append(value); return value; });