如何优雅管理多个Deferred:实现Ajax顺序执行并反馈结果
问题分析
你的代码核心问题在于递归调用时没有将后续Promise的结果传递给外层的Deferred:
- 当执行到
recursiveWhen(next)时,你只是发起了递归,但没有等待它完成,外层的wrapperDeferred从未被resolve,导致上层$.when的then回调永远无法触发。 - 额外问题:
var executable = new pointer(dfd);中不需要使用new,pointer是普通函数,直接调用即可。
修复后的递归方案
修改recursiveWhen函数,确保递归完成后resolve外层的wrapper:
var btnContinue = function (e) { try { var $interactions = $('.interaction', dictionary.$elements.analysis); var autoCloseExecutables = builder.autoCloseExecutableArray($interactions); var recursiveWhen = function (pointer) { var wrapper = new $.Deferred(); var execution = function () { var dfd = new $.Deferred(); // 去掉不必要的new pointer(dfd); return dfd.promise(); }; $.when(execution()).then(function (result) { // 失败时直接返回结果 if (result === false) { wrapper.resolve(result); return; } // 没有更多任务时返回最终结果 if (autoCloseExecutables.length === 0) { wrapper.resolve(result); return; } // 递归执行下一个任务,并在完成后resolve外层wrapper var next = autoCloseExecutables.pop(); recursiveWhen(next).then(function(finalResult) { wrapper.resolve(finalResult); }); }); return wrapper.promise(); }; var autoClosePromise = autoCloseExecutables.pop(); $.when(recursiveWhen(autoClosePromise)).then(function (result) { // 现在可以正常触发这里了 console.log('最终结果:', result); }); } catch (exception) { console.error(exception); } };
更优实现方案(基于Promise链式调用)
递归虽然可行,但用Promise链式调用会更简洁易读,避免嵌套:
var btnContinue = function (e) { try { var $interactions = $('.interaction', dictionary.$elements.analysis); var autoCloseExecutables = builder.autoCloseExecutableArray($interactions); // 将executable转换为返回Promise的函数 var wrapExecutable = function(executable) { return new Promise(function(resolve) { executable({ resolve: resolve }); }); }; // 初始化一个已resolve的Promise作为链的起点 var sequencePromise = Promise.resolve(true); // 构建顺序执行的Promise链 while(autoCloseExecutables.length > 0) { var executable = autoCloseExecutables.pop(); sequencePromise = sequencePromise.then(function(prevResult) { // 如果上一步失败,直接返回结果,终止链 if (prevResult === false) { return prevResult; } // 执行当前任务 return wrapExecutable(executable); }); } // 监听最终结果 sequencePromise.then(function(result) { console.log('最终结果:', result); }); } catch (exception) { console.error(exception); } };
如果你的环境支持ES2017+,还可以用async/await实现更直观的顺序执行:
var btnContinue = async function (e) { try { var $interactions = $('.interaction', dictionary.$elements.analysis); var autoCloseExecutables = builder.autoCloseExecutableArray($interactions); var wrapExecutable = function(executable) { return new Promise(function(resolve) { executable({ resolve: resolve }); }); }; let finalResult = true; while(autoCloseExecutables.length > 0 && finalResult !== false) { var executable = autoCloseExecutables.pop(); finalResult = await wrapExecutable(executable); } console.log('最终结果:', finalResult); } catch (exception) { console.error(exception); } };
注:上述优化方案中,我们将原有的
dfd参数替换为直接传递resolve方法,简化了回调逻辑。
内容的提问来源于stack exchange,提问作者Prisoner ZERO
相关产品推荐
相关产品推荐

