我正在使用jQuery' .when()
到wrap an array of promises,以便在所有承诺都得到解决后我可以采取一些行动。
$.when.apply($, requests).done(function () {
console.log(arguments); //it is an array like object which can be looped
var total = 0;
$.each(arguments, function (i, data) {
console.log(data); //data is the value returned by each of the ajax requests
total += data[0]; //if the result of the ajax request is a int value then
});
console.log(total)
});
假设我希望在每个承诺得到解决时得到通知,作为展示进展的一种方式。例如,如果requests
有50个请求并且其中3个已被解析,我希望能够以6%显示进度条。有没有办法使用$.when
,以便它可以返回整体进度,而无需修改内部承诺及其进度事件?
答案 0 :(得分:11)
$.when()
不会为您执行进度通知。注册每个承诺的进度通知,或者您可以制作自己的$.when()
版本,首先在每个承诺上注册完成通知然后调用$.when()
。
$.whenWithProgress = function(arrayOfPromises, progessCallback) {
var cntr = 0;
for (var i = 0; i < arrayOfPromises.length; i++) {
arrayOfPromises[i].done(function() {
progressCallback(++cntr, arrayOfPromises.length);
});
}
return jQuery.when.apply(jQuery, arrayOfPromises);
}
$.whenWithProgress(requests, function(cnt, total) {
console.log("promise " + cnt + " of " + total + " finished");
}).then(function() {
// done handler here
}, function() {
// err handler here
});
我一直在考虑这个问题,而且我有点想要进度通知,jQuery承诺有进步机制而我们没有使用它。这意味着进度通知不像它们那样可扩展。
不幸的是,因为$.when()
返回一个promise(不是延迟的)而你不能在一个承诺上使用.notify()
来触发进度通知,所以上面的代码是最简单的方法。但是,为了使用.progress
通知而不是自定义回调,可以这样做:
$.whenWithProgress = function(arrayOfPromises) {
var cntr = 0, defer = $.Deferred();
for (var i = 0; i < arrayOfPromises.length; i++) {
arrayOfPromises[i].done(function() {
defer.notify(++cntr, arrayOfPromises.length);
});
}
// It is kind of an anti-pattern to use our own deferred and
// then just resolve it when the promise is resolved
// But, we can only call .notify() on a defer so if we want to use that,
// we are forced to make our own deferred
jQuery.when.apply(jQuery, arrayOfPromises).done(function() {
defer.resolveWith(null, arguments);
});
return defer.promise();
}
$.whenWithProgress(requests).then(function() {
// done handler here
}, function() {
// err handler here
}, function(cnt, total) {
// progress handler here
console.log("promise " + cnt + " of " + total + " finished");
});
反对第二个实施的论点是,承诺标准的努力似乎正在逐渐取消以任何方式与承诺相关的进展(进展将有一个单独的机制)。但是,它现在在jQuery中并且可能会持续很长时间(jQuery不符合信函的承诺标准)所以它真的是你选择的方式。
答案 1 :(得分:2)
我认为你不能(或应该)使用$.when
执行此操作,其回调仅用于调用一次 - 您需要一个可以多次调用的回调,因此您可以将其传递给requests
中的每个承诺。例如:
var count = requests.length;
var complete = 0;
function progress(response) {
complete += 1;
showProgress(complete / count);
saveTheResponseSomewhere(response);
if (complete === count) {
doSomeAllDoneAction();
}
}
requests.forEach(function(request) {
request.then(progress);
});
您可以使用progress
的第三个参数为实际的jqXHR .then
通知添加处理。您可能还需要确保结果与相应的请求相关联,这可能涉及回调中的额外闭包。
答案 2 :(得分:2)
保持简单,你可以定义一个通用的经典构造函数。
var Progress = function(promiseArray, reporter) {
this.promiseArray = promiseArray;
this.reporter = reporter || function(){};
this.complete = 0;
$.each(promiseArray, function(i, p) {
p.then(this.increment).then(this.report);
});
};
Progress.prototype.increment = function() {
this.complete += 1;
};
Progress.prototype.report = function() {
return this.reporter(this.complete, this.promiseArray.length);
};
Progress.prototype.get = function() {
return { complete:this.complete , total:this.promiseArray.length};
};
然后,例如进度温度计:
var progObj = new Progress(requests, function(complete, total) {
var scale = 150;
$("selector").css('someProperty', (scale * complete / total) + 'px');
});
或者进行临时调查:
console.log( progObj.get() );
这种方法的好处是可重用性。可以在任意数量的promise数组上调用new Progress()
,每个数组都有自己的reporter
回调。
如果你愿意的话,可以让Progress
返回一个jfriend风格的通知承诺,虽然我不会这样做,因为jfriend已经给出了。
有一些更多的想法Progress
可以被定义为jQuery插件,允许你调用,例如,如下:
$("selector").progress(function(complete, total) {
var scale = 150;
$(this).css('someProperty', (scale * complete / total) + 'px');
});
在某些情况下可能有优势。
答案 3 :(得分:1)
尝试
HTML
<progress id="progress" min="0" max="100"></progress>
<label for="progress"></label>
JS
$(function () {
var arrayOfPromises = $.map(new Array(50), function (v, k) {
return v === undefined ? new $.Deferred(function (dfd) {
$.post("/echo/json/", {
json: JSON.stringify(k)
}).done(function (data, textStatus, jqxhr) {
return dfd.notify(k).resolve([data, textStatus, jqxhr])
});
return dfd.promise()
}) : null
}),
res = [],
count = null;
$.each(arrayOfPromises, function (k, v) {
$.when(v)
.then(function (p) {
console.log(p[1]);
res.push(p);
if (res.length === arrayOfPromises.length) {
console.log(res);
$("label").append(" done!");
}
}, function (jqxhr, textStatus, errorThrown) {
res.push([textStatus, errorThrown, count])
}, function (msg) {
++count;
count = count;
console.log(msg, count);
$("progress").val(count * 2).next().text(count * 2 + "%");
})
})
})
jsfiddle http://jsfiddle.net/guest271314/0kyrdtng/
以前使用替代方法的努力:
HTML
<progress id="progress" value="0" max="100"></progress>
<output for="progress"></output>
JS
$(function () {
$.ajaxSetup({
beforeSend: function (jqxhr, settings) {
var prog = $("#progress");
jqxhr.dfd = new $.Deferred();
jqxhr.dfd.progress(function (data, _state) {
prog.val(data)
.siblings("output[for=progress]")
.text(prog.val() + "% " + _state);
if (_state === ("resolved" || "rejected")) {
prog.val(100);
window.clearInterval(s);
};
});
var count = 0.000001;
jqxhr.counter = function (j) {
this.dfd.notify(Math.ceil(count), this.state());
++count;
console.log(this.state(), prog.prop("value"));
};
var s = setInterval($.proxy(jqxhr.counter, jqxhr, jqxhr), 15);
}
});
$.post("/echo/json/", {
json: JSON.stringify({
"defer": new Array(10000)
})
})
.always(function (data, textStatus, jqxhr) {
console.log(data, jqxhr.state(), $("#progress").val());
});
})