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

图片转字符串后Promise异步请求导致循环无法退出的问题求解

问题与解决方案:异步Promise导致循环无法退出

问题描述

我用OpenCV将图片转换为字符串(执照号),每三次操作能得到正确的目标字符串。之后通过testerLicence函数(基于XMLHttpRequest封装的Promise)将字符串发送至PHP接口,查询数据库中是否存在该执照号。
主程序中设置了while循环,预期当执照号存在时退出循环,但即使查询结果为true,循环也无法退出,怀疑是Promise的异步特性导致,但无法定位具体原因,请求改进代码并解决问题。

原代码

主程序代码

// 图片处理与执照验证循环
boutonPhoto.addEventListener('click', () => {
  var endLoop = false;
  while (endLoop != true) {
    try {
      // 处理图片得到执照号 uneLicence(此处为示意代码)
      // Process image to String uneLicence
      testerLicence(uneLicence).then((boolIfExist) => {
        console.log(boolIfExist);
        endLoop = boolIfExist
      });
    } catch (e) {
      // 图片处理出错时,继续循环
      endLoop = false;
    }
  }
});

testerLicence函数代码

function testerLicence(numLicence) {
  return new Promise((resolve) => {
    // 创建XMLHttpRequest对象
    const xhr = new XMLHttpRequest();
    const url = '../php/api.php';

    xhr.open('POST', url);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    const data = 'license=' + numLicence.getNumLicence();

    console.log(data);
    xhr.send(data);

    // 处理响应
    xhr.onreadystatechange = function () {
      if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
        const response = JSON.parse(this.responseText);
        resolve(response.licenseExists);
      }
    };
  });
}

PHP接口代码

<?php
// 获取请求中的执照号
$license = $_POST['license'];

// 查询数据库验证执照号是否存在
$conn = new PDO('mysql:host=localhost;dbname=bd_sporttrack;charset=utf8','root','root');
$req = "SELECT * FROM Joueur WHERE licence = :licence";
$req = $conn->prepare($req);
$req->execute(['licence'=>$license]);
$res = $req->fetchAll();

// 返回验证结果
$response = ['licenseExists' => count($res) > 0];
print(json_encode($response)); 
?>

问题根源

JavaScript是单线程运行的:

  • 同步的while循环会持续占据主线程,异步Promise的回调(then中的代码)会被放到任务队列中,只有主线程空闲时才会执行。
  • 原代码的while循环永远不会释放主线程,导致endLoop = boolIfExist根本没有执行的机会,循环永远无法退出。
  • 即使回调能执行,while循环的条件判断是同步的,不会等待异步请求完成,每次循环都会直接进入下一轮,完全忽略查询结果。

改进方案

1. 用async/await实现异步循环

将点击事件处理函数改为异步函数,配合await等待Promise结果,避免同步循环阻塞主线程:

boutonPhoto.addEventListener('click', async () => {
  while (true) {
    try {
      // 替换为你实际的图片处理逻辑,生成uneLicence
      const uneLicence = processImageToLicence(); 
      
      // 等待执照验证结果
      const boolIfExist = await testerLicence(uneLicence);
      console.log(boolIfExist);
      
      // 执照存在则退出循环
      if (boolIfExist) {
        console.log("找到有效执照,退出循环");
        break;
      }
    } catch (e) {
      console.error("图片处理或请求出错:", e);
    }
    // 可选:添加500ms延迟,避免高频操作占用资源
    await new Promise(resolve => setTimeout(resolve, 500));
  }
});

2. 优化testerLicence函数

添加错误处理,避免请求失败时Promise永远挂起:

function testerLicence(numLicence) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const url = '../php/api.php';

    xhr.open('POST', url);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    const data = 'license=' + encodeURIComponent(numLicence.getNumLicence()); // 编码避免特殊字符问题

    console.log(data);
    xhr.send(data);

    xhr.onreadystatechange = function () {
      if (this.readyState === XMLHttpRequest.DONE) {
        if (this.status === 200) {
          try {
            const response = JSON.parse(this.responseText);
            resolve(response.licenseExists);
          } catch (parseErr) {
            reject(new Error("JSON解析失败:" + parseErr.message));
          }
        } else {
          reject(new Error("请求失败,状态码:" + this.status));
        }
      }
    };

    // 处理网络错误与超时
    xhr.onerror = () => reject(new Error("网络请求失败"));
    xhr.timeout = 5000; // 5秒超时
    xhr.ontimeout = () => reject(new Error("请求超时"));
  });
}

3. 优化PHP接口

提升查询效率,添加错误处理:

<?php
// 安全获取POST参数,避免未定义索引错误
$license = isset($_POST['license']) ? trim($_POST['license']) : '';

if (empty($license)) {
  echo json_encode(['licenseExists' => false]);
  exit;
}

try {
  $conn = new PDO('mysql:host=localhost;dbname=bd_sporttrack;charset=utf8','root','root');
  $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
  // 只查询是否存在,无需返回所有数据,提升效率
  $stmt = $conn->prepare("SELECT 1 FROM Joueur WHERE licence = :licence LIMIT 1");
  $stmt->execute(['licence' => $license]);
  
  // 用rowCount判断是否有匹配结果
  $response = ['licenseExists' => $stmt->rowCount() > 0];
} catch(PDOException $e) {
  // 数据库出错时返回false,或根据需求处理
  $response = ['licenseExists' => false];
}

header('Content-Type: application/json');
echo json_encode($response);
?>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 15:01:11