You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确链式处理JS嵌套AJAX与Promise?解决ajaxStop触发异常

问题描述

我需要按顺序执行以下操作:

  1. 发起一个AJAX调用,处理数据后将其存储到IndexedDB(使用基于Promise的idb-keyval库)
  2. 根据数据内容发起另一个AJAX调用获取依赖数据,同样存储到IndexedDB
  3. 最后触发文档的最终数据处理

我尝试用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 09:52:02