如何在不嵌套的情况下对异步函数组进行排序.当区间)
How to sequence groups of async functions without nesting (jquery $.when .then)
我有一组异步函数,每一组都依赖于前一组的结果,但在一个组内,函数可以按任何顺序执行/解析。
如何按顺序调用函数组而不为每个组设置嵌套级别?
function waste(secs)
{
var prom=$.Deferred();
setTimeout(function(){
$('body').append('<p>'+secs+'</p>');
prom.resolve();
}, secs*1000);
return prom;
}
$.when(
waste(5),
waste(4)
).then(function(){
$.when(
waste(3),
waste(2)
).then(function(){
$.when(
waste(1),
waste(0)
).then(function(){
$('body').append('<p>done</p>');
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
以下是我到目前为止所发现的,但它在每个部分都有一种尴尬的return $.when(
缩进:
function waste(secs)
{
var prom=$.Deferred();
setTimeout(function(){
$('body').append('<p>'+secs+'</p>');
prom.resolve();
}, secs*1000);
return prom;
}
$.when(
waste(5),
waste(4)
).then(function(){
return $.when(
waste(3),
waste(2)
);
}).then(function(){
return $.when(
waste(1),
waste(0)
);
}).then(function(){
$('body').append('<p>done</p>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
对异步函数组进行排序的最佳实践方法是什么,其中组中的所有方法需要在下一个组执行之前完成?
我想理想情况下,像这样的事情是我希望工作:
$.when(
waste(5),
waste(4)
).when(
waste(3),
waste(2)
).when(
waste(1),
waste(0)
).then(function(){
$('body').append('<p>done</p>');
});
从另一个角度解决这个问题:您希望将哪些输入映射到哪些输出?让我们看看我们是否能把你的问题推广到这一点。
基本上你有一个并行操作集合的序列。
每个操作都可以用一个函数和一个参数列表来表示,等待在适当的时间到来时执行。我们将这个抽象命名为callDef:
[func, arg1, arg2, arg3]
或者,对于你的例子来说:
[waste, 5]
运行它的实用程序很简单:
function call(callDef) {
return callDef[0].apply(null, callDef.slice(1));
}
现在,每组操作都可以表示为callDef 本身,假设我们有一个
callInParallel
函数,该函数接受callDef :
的列表。[callInParallel, [callDef, callDef]]
或
[ callInParallel, [[waste, 5], [waste, 4]] ],
callInParallel
函数同样简单:
function callInParallel(callDefs) {
return $.when.apply($, callDefs.map(call));
}
所以你的整个输入可以表示为嵌套的callDefs:
[
[ callInParallel, [[waste, 5], [waste, 4]] ],
[ callInParallel, [[waste, 3], [waste, 2]] ],
[ callInParallel, [[waste, 1], [waste, 0]] ]
]
现在我们可以通过call
单独执行叶子callDefs,并通过callInParallel
并行执行它们的每组。
缺失的位是callInSequence
函数,在前一个callDef完成后执行每个callDef。
由于callInParallel
返回一个promise,因此可以将其用作执行下一个callDef的先决条件:我们需要将前一个操作的结果存储在中间的wait
变量中,并通过.then()
将下一个操作链接到它。之后,我们可以用当前操作覆盖wait
,有效地为下一步做准备。
当所有步骤被链接时,我们可以返回一个总承诺,同样通过$.when
,总承诺在最后一个之前不解决,在倒数第二个之前不解决,以此类推:
function callInSequence(callDefs) {
var wait = $.Deferred().resolve();
return $.when.apply($, callDefs.map(function (callDef) {
return wait = wait.then(function () {
return call(callDef);
});
}));
}
由于抽象允许,我们可以用一个callDef:
来表示整个操作。[
callInSequence, [
[ callInParallel, [[waste, 5], [waste, 4]] ],
[ callInParallel, [[waste, 3], [waste, 2]] ],
[ callInParallel, [[waste, 1], [waste, 0]] ]
]
]
把它们放在一起:
function waste(secs) {
var result = $.Deferred();
setTimeout(function () {
$('body').append('<div>'+secs+'</div>');
result.resolve('done ' + secs);
}, secs * 1000);
return result.promise();
}
function call(callDef) {
return callDef[0].apply(null, callDef.slice(1));
}
function callInParallel(callDefs) {
return $.when.apply($, callDefs.map(call));
}
function callInSequence(callDefs) {
var wait = $.Deferred().resolve();
return $.when.apply($, callDefs.map(function (callDef) {
return wait = wait.then(function () {
return call(callDef);
});
}));
}
// ---------------------------------------------------------------------------
var everything = [
callInSequence, [
[ callInParallel, [[waste, 5], [waste, 4]] ],
[ callInParallel, [[waste, 3], [waste, 2]] ],
[ callInParallel, [[waste, 1], [waste, 0]] ]
]
];
call(everything).done(function () {
$('<hr>').appendTo('body');
$('<pre>', {text: JSON.stringify(arguments, null, 2)}).appendTo('body');
}).fail(function (error) {
console.log(error);
});
$('<div>sequence has started...</div>').appendTo('body');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
指出:
- 这个代码需要jQuery 1.8+,因为延迟的行为在1.8中发生了变化。
你可以用不同的方式组合:
var everything = [ callInSequence, [ [ callInSequence, [[waste, 5], [waste, 4]] ], [ callInParallel, [[waste, 3], [waste, 2]] ], [ callInSequence, [[waste, 1], [waste, 0]] ] ] ];
- 上面的
thisArg
没有处理,每个函数都将针对全局对象(window
)调用。我把实现它作为一个练习。有两种方法可以做到这一点,一种是使用.bind()
,另一种是扩展callDef
的定义。
不确定如果请求失败将如何工作,
function waste(secs) {
var prom = $.Deferred();
setTimeout(function() {
$('body').append('<p>' + secs + '</p>');
prom.resolve();
}, secs * 1000);
return prom.promise();
}
// define error handler
var err = function err(e) {
console.log(e)
};
$.when(
waste(5),
waste(4)
).then(function() {
return $.when(
waste(3),
waste(2)
).fail(err);
}, err).then(function() {
return $.when(
waste(1),
waste(0)
).fail(err);
}, err).then(function() {
$('body').append('<p>done</p>');
}, err);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
我假设$.when
相当于Promise.all
。在这种情况下,你所做的是正确的。我认为你真正能做的就是加入你的一些台词来提高美感。
$.when(waste(5),waste(4))
.then(function() { return $.when(waste(3), waste(2)); })
.then(function() { return $.when(waste(1), waste(0)); })
.then(function(){
$('body').append('<p>done</p>');
}, errorHandler);
是否有你使用jQuery的原因?这可以通过Promises原生实现:
function append(secs) {
return Promise(function(resolve, reject) {
setTimeout(function(){
$('body').append('<p>'+secs+'</p>');
resolve();
}, !isNaN(secs) && secs*1000);
});
}
Promise.all([append(5), append(4)])
.then(function() { return Promise.all([append(3), append(2)]); })
.then(function() { return Promise.all([append(1), append(0)]); })
.then(function() { return append('done'); }, errorHandler);
- JavaScript数组排序(函数)用于对表行进行排序,而不是排序
- Javascript中的多维数组排序索引问题
- 如何按日期对Javascript对象数组排序
- 数组排序后显示更改
- 当许多元素相等时,Javascript数组排序无法正常工作
- Javascript数组排序速度受字符串长度的影响
- 按唯一键将对象数组排序为数组
- 以 js 为单位的数组排序
- js 数组排序无法正常工作
- 数组排序.论点从何而来
- 不需要的数组排序
- 数组排序不正确/不可预测,使用 indexOf 时
- Javascript将数组排序到树中
- 随后的多维数组排序会产生意外的结果
- 数组排序基于纯javascript搜索文本匹配
- Javascript自定义数组按数组排序
- 使用字符串按属性错误对对象进行数组排序
- 多维数组排序
- JavaScript与PHP在数组排序中的对比
- Javascript-关联数组排序