在日常开发,经常会有要求异步函数的执行结果按顺序执行,看下面的例子:
let asyncF = (i, callback) => { 2 let lateTime = Math.random() * 10; setTimeout(() => {callback(i)}, lateTime); }
for (var i = 0; i < 5; i ++) { asyncF(i, console.log); }
|
这个例子模拟了一系列的异步调用,它们的执行时间不确定,所以完全无法预测多次调用的结果,对于这类操作通常有2种解决方案:
缓存所有结果,然后按顺序执行
如果每个发出的请求返回内容都很有用,而且需要保持一定的顺序,可以使用这种方案。
function sequenceAll(func) { 2 let callUid = 0; let nextCallId = 0; let taskQueue = []; const runnerNext = (runId) => { if (taskQueue[runId]) { taskQueue[runId](); runnerNext(runId + 1); } } return function(...args) { let currCallId = callUid; callUid += 1; taskQueue.push(undefined); const callback = args.pop(); return func(...args, function(...iargs) { if (currCallId === nextCallId) { if (callback && typeof callback === 'function') { callback(...iargs); } nextCallId += 1; runnerNext(nextCallId); } else { taskQueue[currCallId] = () => { callback(...iargs); nextCallId += 1; }; } }); } }
let asyncFSeq = sequenceAll(asyncF); for (var i = 0; i < 50; i ++) { asyncFSeq(i, console.log); }
|
直接执行,忽略不合时序的结果
如果在执行异步操作拿到结果准备回调后发现已经有晚于其发出的异步操作的回调已经被执行,name 忽略本函数的调用结果。
function sequenceIgnore(func) { 2 let callUid = 0; let lastCallId = 0; return function(...args) { let currCallId = callUid; callUid += 1; const callback = args.pop() return func(...args, function(...args) { if (currCallId < lastCallId) { return; } lastCallId = currCallId; if (callback && typeof callback === 'function') { callback(...iargs); } }); } }
const asyncFSeq = sequenceIgnore(asyncF);
for (var i = 0; i < 5; i ++) { asyncFSeq(i, console.log); }
|
这种处理方法更适合于处理只需要最晚发出的执行结果的操作。比如根据用户输入内容实时发出请求进行模糊匹配,这个时候呈现给用户的自然是与用户最后输入的文本匹配的结果是最好的处理方案。