我有一系列顺序执行的异步函数。所以我使用promises来创建promise队列。不幸的是,在Linux上的Firefox 25.0.1上,我遇到了too much recursion
JavaScript错误。它在Mac上的相同Firefox版本上运行良好,它也适用于chrome。但我需要它在任何地方工作,包括Linux。您能否建议更好地实施executePromiseQueueSync
功能?
//-----------------------------------------------------------------------------------------
function executePromiseQueueSync(queue){
var seed = $.Deferred();
var acc = seed;
for (var i = 0; i < queue.length; ++i )
{
var promise = queue[i];
acc = acc.then(promise.funct.apply(null, promise.argmnt));
}
seed.resolve();
return acc;
}
//-----------------------------------------------------------------------------------------
function someTask(){
var dfd = new jQuery.Deferred();
dfd.notify();
dfd.resolve();
return dfd.promise();
}
//-----------------------------------------------------------------------------------------
$(function(){
var promisesQueue = []
for(var i = 0; i < 200; ++i){
promisesQueue.push({funct:someTask, argmnt:[]});
}
executePromiseQueueSync(promisesQueue).then(function(){alert('done!');});
});
//-----------------------------------------------------------------------------------------
JSFiddle示例:http://jsfiddle.net/C2YN4/4/
到目前为止我有什么(我对其他命题和更正非常开放):
function executePromiseQueueSync(queue){
if (queue.length > TRESHOLD){
var i,j;
var shortQueue = []
for (i=0,j=queue.length; i<j; i+=TRESHOLD) {
temparray = queue.slice(i, i+TRESHOLD);
shortQueue.push({funct:executePromiseQueueSync, argmnt:[temparray]});
}
return executePromiseQueueSync(shortQueue);
}
var seed = $.Deferred();
var acc = seed;
for (var i = 0; i < queue.length; ++i )
{
var promise = queue[i];
acc = acc.then(promise.funct.apply(null, promise.argmnt));
}
seed.resolve();
return acc;
}
所以基本的想法是建立一个承诺树而不是承诺链。这样我们就不会深入到堆栈中。
答案 0 :(得分:2)
你可以使用jQuery.when
〜虽然在200多个论点中传递可能有点疯狂:
使用jQuery的JavaScript
var resolves = [];
function log(msg){
$('#log').append('<div><small>' + msg + '</small></div>');
}
function someTask(i){
var dfd = new $.Deferred();
log('created task '+i);
resolves.push(function(){
log('resolved task '+i);
dfd.resolve();
});
return dfd.promise();
}
$(function(){
$('#resolve1').click(function(){
for ( var i=0; i<resolves.length; i++ ) {
resolves[i]();
}
});
$('#resolve2').click(function(){
for ( var i=0; i<resolves.length; i++ ) {
if ( i == 5 ) continue;
resolves[i]();
}
});
$('#resolve3').click(function(){
resolves[5]();
});
var i, queue = []; for(i=0; i<200; ++i){ queue.push(someTask(i)); }
(jQuery.when.apply(jQuery, queue))
.done(function(){
log('all resolved!');
alert('all resolved!');
})
;
});
此示例的标记
<button id="resolve1">Resolve all</button>
<button id="resolve2">Resolve (all but one)</button>
<button id="resolve3">Resolve (the remaining one)</button>
<div id="log"></div>
此示例的CSS
#log {
border: 1px solid black;
padding: 10px;
width: 400px;
height: 200px;
overflow: auto;
}
以上代码大部分只是为了说明,关键点是:
var i, queue = []; for(i=0; i<200; ++i){ queue.push(someTask(i)); }
(jQuery.when.apply(jQuery, queue))
.done(function(){
log('all resolved!');
alert('all resolved!');
})
;
上面生成一个promise对象数组,然后使用apply
将它们传递给jQuery.when
,done
然后处理创建正确的结构,以便在它们全部完成后触发function log(msg){
$('#log').append('<div><small>' + msg + '</small></div>');
}
function someTask(i){
var dfd = new $.Deferred();
log(':: created task '+i);
setTimeout(function(){
log(':: resolved task '+i);
dfd.resolve();
},50);
return dfd.promise();
}
$(function(){
var Queue;
Queue = function( items ){
this.items = items.slice(0);
this.promise = $.Deferred();
};
Queue.prototype.next = function(){
log(':: next task triggered');
var q = this;
q.lastItem = q.items.shift();
if ( q.lastItem ) {
q.lastPromise = q.lastItem.func.apply( null, q.lastItem.args );
q.lastPromise.then(function(){
/// include a setTimeout 0 to avoid possible stack/recursion errors.
setTimeout(function(){
q.next();
},0);
});
}
else {
/// we are finished
q.promise.resolve();
}
};
Queue.prototype.run = function(){
this.next();
};
var i, items = []; for(i=0; i<200; ++i){
items.push({ func: someTask, args:[i] });
}
var q = new Queue( items );
q.promise.done(function(){
log(':: done!');
alert('Done!');
});
q.run();
});
回调。那就是你想要这种行为。如果你想要一个等待每个promise对象按顺序解析的系统,在触发下一个任务之前,你需要一些不同的东西。
就按顺序发射任务而言,你需要一种不同的方法,如下所示:
Queue
这构建了一个定制的$.Deferred()
对象,用于跟踪承诺列表,在第一个成功后,触发下一个。但是,这段代码显然需要进行错误处理。
您不能依赖每个承诺的进度,因为任何时候只有一个被触发。您可以将自己的通知调用添加到整个var Queue;
Queue = function( items ){
this.items = items.slice(0);
this.promise = $.Deferred();
this.count = 0;
};
Queue.prototype.next = function(){
log(':: next task triggered');
var q = this;
q.lastItem = q.items.shift();
if ( q.lastItem ) {
q.lastPromise = q.lastItem.func.apply( null, q.lastItem.args );
q.lastPromise.then(function(){
q.promise.notify(q.count++);
q.next();
});
}
else {
q.promise.resolve();
}
};
Queue.prototype.run = function(){
this.next();
};
var q = new Queue( items );
q.promise
.done(function(){log(':: done!');})
.progress(function(p){log('::progress ' + p);})
;
q.run();
对象中。
{{1}}
答案 1 :(得分:1)
FWIW ......我有点晚了,但这是我对你的队列要求的实现:
function RunnablePromise(func, args, thisArg) {
var def = $.Deferred(), prm = def.promise();
prm.run = function () {
func.apply(thisArg, args).done(def.resolve).fail(def.reject);
return prm;
};
return prm;
}
function PromiseQueue() {
var q = [], overall = $.Deferred(), self = this;
this.run = function () {
var runnable = q.shift();
if (runnable) {
overall.notify(q.length);
runnable.run().done(self.run).fail(overall.reject);
} else {
overall.resolve();
}
return overall.promise();
};
this.append = function (task, args, thisArg) {
var runnable = new RunnablePromise(task, args, thisArg);
q.push(runnable);
return runnable;
};
}
像这样使用:
var pq = new PromiseQueue(), i,
success = function (i) { console.log("task done: " + i); };
for (i = 0; i < 200; i++) {
// .append() returns the individual task's promise
pq.append(someAsyncTask, [i]).done(success);
}
// .run() returns the entire queue's promise
pq.run().progress(function (remain) {
console.log("remaining: " + remain);
}).done(function () {
console.log("all done!");
}).fail(function () {
console.log("error!");
});