如何正确链式处理JS嵌套AJAX与Promise?解决ajaxStop触发异常
问题描述
我需要按顺序执行以下操作:
- 发起一个AJAX调用,处理数据后将其存储到IndexedDB(使用基于Promise的idb-keyval库)
- 根据数据内容发起另一个AJAX调用获取依赖数据,同样存储到IndexedDB
- 最后触发文档的最终数据处理
我尝试用ajaxStop事件在所有AJAX调用完成时触发,但执行顺序不符合预期:ajaxStop在主AJAX调用完成时就触发,未等待子AJAX调用完成;子AJAX完成后又再次触发。
原代码
import { set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@6/+esm'; $(document).on("ajaxStop", function() { console.log("All Done!"); }); $(document).ready(function () { $.ajax({ url: '/echo/json/', async: true, beforeSend: function(){ console.log("Sending main request!"); }, success: function () { set('test', 'test').then(() => { $.ajax({ url: '/echo/json/', async: true, beforeSend: function(){ console.log("Sending sub-request!"); }, success: function() { set('sub-test', 'sub-test'); console.log("Sub-request done!"); } }); }); } }); });
控制台输出

请问如何让ajaxStop等待所有调用完成?或者让主AJAX调用等待success函数中的所有处理完成后再报告完成?
解决方案
方案一:用Promise链式调用(推荐)
放弃依赖全局的ajaxStop事件,直接用Promise(结合async/await)串联所有异步操作,手动控制最终处理的时机,代码逻辑更清晰可控。
import { set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@6/+esm'; // 封装jQuery AJAX为Promise格式 function ajaxPromise(options) { return new Promise((resolve, reject) => { $.ajax({ ...options, success: resolve, error: reject }); }); } $(document).ready(async function () { try { // 1. 执行主AJAX请求 console.log("Sending main request!"); await ajaxPromise({ url: '/echo/json/' }); // 2. 存储主数据到IndexedDB await set('test', 'test'); // 3. 执行子AJAX请求 console.log("Sending sub-request!"); await ajaxPromise({ url: '/echo/json/' }); // 4. 存储子数据到IndexedDB await set('sub-test', 'sub-test'); console.log("Sub-request done!"); // 所有操作完成,触发最终处理 console.log("All Done!"); } catch (err) { console.error('请求或存储出错:', err); } });
方案二:关联主AJAX的Deferred对象(适配ajaxStop)
如果一定要保留ajaxStop,可以通过jQuery AJAX返回的Deferred对象,将后续的异步操作(IndexedDB存储、子AJAX)串联到主AJAX的执行链中,让jQuery的AJAX队列认为所有操作是一个整体,仅在全部完成后触发一次ajaxStop。
import { set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@6/+esm'; $(document).on("ajaxStop", function() { console.log("All Done!"); }); $(document).ready(function () { // 发起主AJAX并保留Deferred对象 const mainAjax = $.ajax({ url: '/echo/json/', async: true, beforeSend: function(){ console.log("Sending main request!"); } }); // 串联后续异步操作,让主AJAX的完成时机包含所有步骤 mainAjax .then(() => set('test', 'test')) // 等待主数据存储完成 .then(() => { // 发起子AJAX并返回其Deferred对象,让链等待它完成 return $.ajax({ url: '/echo/json/', async: true, beforeSend: function(){ console.log("Sending sub-request!"); }, success: function() { console.log("Sub-request done!"); } }); }) .then(() => set('sub-test', 'sub-test')); // 等待子数据存储完成 });
原代码问题说明
原代码中,主AJAX的success回调里的异步操作(IndexedDB存储、子AJAX)没有和主AJAX的Deferred对象关联,主AJAX完成后就会触发ajaxStop;而子AJAX是后续独立发起的,完成后又会再次触发ajaxStop,导致两次输出“All Done!”。
内容的提问来源于stack exchange,提问作者Oleksandr
相关产品推荐
相关产品推荐

