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

异步函数执行顺序异常:对象未填充完成后续函数已执行

异步函数执行顺序问题的解决方案

问题根源

queryPermissionsId()内部的数据库查询是异步回调操作,但这个函数没有提供任何等待异步操作完成的机制。当你在main.js里调用它后,JavaScript引擎会立即继续执行后续的console.log、profileData.testAccount()等同步代码,此时数据库查询还未返回结果,pfData的权限属性自然还是undefined。

JavaScript的单线程特性决定了:异步任务(比如数据库请求)会被放入事件队列,只有当前同步代码全部执行完毕后,才会处理队列里的异步回调。这就是你看到控制台输出顺序不符合预期的原因。

解决方案:将异步回调改造为Promise

要让后续逻辑等待queryPermissionsId()完成,需要把它改造成返回Promise的函数,然后通过async/await或.then()来控制执行顺序。

方案1:使用async/await(推荐,代码更简洁)

1. 改造profileData.js的queryPermissionsId

先把回调式的connection.query转成Promise版本(Node环境下用util.promisify):

// 引入util模块(如果还没引入)
const util = require('util');
// 把connection.query转成Promise版本
const queryAsync = util.promisify(connection.query).bind(connection);

async function queryPermissionsId() {
  try {
    // 第一个查询:获取accountId
    const accountQuery = "SELECT id FROM `accounts` WHERE email = ?";
    console.log(accountQuery, [pfData.email]);
    const accountResult = await queryAsync(accountQuery, [pfData.email]);
    console.log(accountResult);
    
    const accountId = accountResult[0].id;
    
    // 第二个查询:获取权限数据
    const permQuery = "SELECT * FROM `permissions` WHERE accountId = ?";
    const permissionsResult = await queryAsync(permQuery, [accountId]);
    console.log(permissionsResult[0]);
    
    // 填充pfData的权限属性
    const permData = permissionsResult[0];
    pfData.accountId = permData.accountId;
    pfData.can_see_inventory = permData.can_see_inventory;
    pfData.can_see_employees = permData.can_see_employees;
    pfData.can_see_past_orders = permData.can_see_past_orders;
    pfData.can_see_open_orders = permData.can_see_open_orders;
    pfData.can_create_order = permData.can_create_order;
    pfData.can_change_privialages = permData.can_change_privialages;
    pfData.can_create_inventory_items = permData.can_create_inventory_items;
    pfData.can_create_inventory_categories = permData.can_create_inventory_categories;
    pfData.is_admin = permData.is_admin;

    console.log(pfData);
  } catch (err) {
    console.error('查询权限失败:', err);
  }
}

2. 修改main.js的调用逻辑

用async/await等待queryPermissionsId完成:

profileData.getProfile()
  .then(async () => {
    $(document).ready(async function() {
      // 等待权限查询完成
      await profileData.queryPermissionsId();
      
      // 后续逻辑现在会在pfData填充完成后执行
      loadNav();
      console.log(profileData.pfData);
      console.log(profileData.pfData.is_admin);
      profileData.testAccount();
      // 创建页面元素
    });
  })

方案2:手动封装Promise(不依赖async/await)

如果环境不支持async/await,可以手动把queryPermissionsId封装成Promise:

function queryPermissionsId() {
  return new Promise((resolve, reject) => {
    // 第一个查询
    const accountQuery = "SELECT id FROM `accounts` WHERE email = ?";
    console.log(accountQuery, [pfData.email]);
    connection.query(accountQuery, [pfData.email], function(err, accountResult) {
      if (err) {
        console.error(err);
        return reject(err);
      }
      console.log(accountResult);
      
      const accountId = accountResult[0].id;
      // 第二个查询
      const permQuery = "SELECT * FROM `permissions` WHERE accountId = ?";
      connection.query(permQuery, [accountId], function(err, permissionsResult) {
        if (err) {
          console.error(err);
          return reject(err);
        }
        console.log(permissionsResult[0]);
        
        // 填充pfData
        const permData = permissionsResult[0];
        pfData.accountId = permData.accountId;
        pfData.can_see_inventory = permData.can_see_inventory;
        pfData.can_see_employees = permData.can_see_employees;
        pfData.can_see_past_orders = permData.can_see_past_orders;
        pfData.can_see_open_orders = permData.can_see_open_orders;
        pfData.can_create_order = permData.can_create_order;
        pfData.can_change_privialages = permData.can_change_privialages;
        pfData.can_create_inventory_items = permData.can_create_inventory_items;
        pfData.can_create_inventory_categories = permData.can_create_inventory_categories;
        pfData.is_admin = permData.is_admin;

        console.log(pfData);
        resolve();
      });
    });
  });
}

然后main.js里用.then()等待:

profileData.getProfile()
  .then(() => {
    $(document).ready(function() {
      profileData.queryPermissionsId()
        .then(() => {
          loadNav();
          console.log(profileData.pfData);
          console.log(profileData.pfData.is_admin);
          profileData.testAccount();
          // 创建页面元素
        })
        .catch(err => console.error(err));
    });
  })

额外提醒:避免SQL注入

原来的代码直接拼接SQL字符串,存在严重的SQL注入风险。上面的改造中已经改用参数化查询(用?作为占位符,把参数放在数组里传递),这是操作数据库的最佳实践,务必保留这个写法。


内容的提问来源于stack exchange,提问作者Wadert3

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:35:25