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

AS3中多URLLoader请求数据串位问题排查与处理咨询

问题分析:Flash中sqlReq类多请求数据混乱/返回null的问题

问题描述

封装的sqlReq类用于服务器数据库交互,单独发起请求时运行正常,但连续触发多个请求(如同时调用getName()和getAge())时,会出现第二个请求获取前一次请求数据、甚至返回null的异常。添加延迟定时器可临时解决,但业务需要支持无延迟的多请求并获取各自正确结果。

原sqlReq类代码

package {
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.events.*;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLVariables;
import flash.net.URLRequestMethod;
import flash.net.URLLoaderDataFormat;


public class sqlReq extends URLLoader {

    private var phpVars: URLVariables;
    private var phpFileRequest: URLRequest;
    private var phpLoader: URLLoader;
    private var tempPhpVars: String;
    private var totalBytes: uint;
    
    public function sqlReq(link: String, dataVars: String) {
        // constructor code

        phpVars = new URLVariables();
        phpFileRequest = new URLRequest(link);
        phpFileRequest.method = URLRequestMethod.POST;
        phpLoader = new URLLoader();


        phpLoader.dataFormat = URLLoaderDataFormat.TEXT;
        phpLoader.addEventListener(Event.COMPLETE, phpReqCompleted);
        phpLoader.addEventListener(IOErrorEvent.IO_ERROR, phpReqError);
        phpLoader.addEventListener(IOErrorEvent.NETWORK_ERROR, phpReqNetworkError);
        phpLoader.addEventListener(ProgressEvent.PROGRESS, phpReqprogress);
        //phpLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, phpReqErrorhttpStatus);


        phpFileRequest.data = dataVars;
        phpLoader.load(phpFileRequest);
    }


    private function phpReqCompleted(event: Event): void {

        //trace("returned vars",event.target.data);
        tempPhpVars = event.target.data;
        this.dispatchEvent(new Event(Event.COMPLETE));
        clearEvents();

    }

    private function phpReqError(event: IOErrorEvent): void {
        this.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR));

        tempPhpVars = "resultIs:" + event.target.data;
        //trace("Code Confirmed: " + event);

    }
    public function phpReqResults(): String {
        return tempPhpVars;
    }
    public function ByteAmount(): uint {
    
        return totalBytes;
        
    }
    private function phpReqprogress(event: ProgressEvent): void {
        //trace(event.bytesLoaded, "total ", event.bytesTotal);
        totalBytes = event.bytesTotal;
        this.dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS));

    }
    private function phpReqNetworkError(event: IOErrorEvent): void {
        this.dispatchEvent(new IOErrorEvent(IOErrorEvent.NETWORK_ERROR));
        //phpVars = event.target.data;
        //trace("Code Confirmed: " + event);
    }
    private function clearEvents(): void {
        phpLoader.removeEventListener(Event.COMPLETE, phpReqCompleted);
        phpLoader.removeEventListener(IOErrorEvent.IO_ERROR, phpReqError);
        phpLoader.removeEventListener(IOErrorEvent.NETWORK_ERROR, phpReqNetworkError);
        phpLoader.removeEventListener(ProgressEvent.PROGRESS, phpReqprogress);
        //phpLoader.removeEventListener(HTTPStatusEvent.HTTP_STATUS, phpReqErrorhttpStatus);
        phpVars = null;
        phpLoader = null;
        phpFileRequest = null;

    }
}
}

原调用代码

var sql:sqlReq;

var myTimer: Timer = new Timer(500, 20);

myTimer.addEventListener(TimerEvent.TIMER, onTick);
myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimeUp);

function getName():void{
    sql= new sqlReq("http://mysite.php","&user=Sarah");
    sql.addEventListener(Event.COMPLETE,gotMyName);
    sql.addEventListener(IOErrorEvent.IO_ERROR,ErrorGettingMyName);
}
function getAge():void{
    sql= new sqlReq("http://mysite.php","&age=32");
    sql.addEventListener(Event.COMPLETE,gotMyAge);
    sql.addEventListener(IOErrorEvent.IO_ERROR,ErrorGettingMyAge);
}
function gotMyName(event:Event):void{
   //All good
}
 function gotMyAge(event:Event):void{
   //All good
}
function ErrorGettingMyName(event:IOErrorEvent):void{
   //All bad
}
function ErrorGettingMyAge(event:IOErrorEvent):void{
   //All bad
}

function onTick(event:TimerEvent):void{

    getName();
    getAge();
}
function onTimeUp(event:TimerEvent):void{
     //clear and remove timer events
}

核心问题原因

  • 全局变量覆盖:调用代码中的sql是全局变量,getName()和getAge()连续执行时,后创建的sqlReq实例会直接覆盖前一个。前一个请求的事件监听虽然绑定了,但后续回调时访问的是被覆盖后的新实例,导致前一个请求的结果无法正确获取,甚至返回null。
  • 类设计冗余:sqlReq继承了URLLoader,但内部又维护了一个独立的phpLoader实例,属于冗余设计。这种双层URLLoader的结构会导致事件转发和状态管理混乱,多个并行请求的tempPhpVars容易互相干扰。
  • 资源清理时机不当:phpReqCompleted中直接调用clearEvents()将内部phpLoader置空,如果多个请求并行完成,可能导致未完成的请求资源被错误释放,引发数据异常。

修复方案

1. 修复调用代码:取消全局变量,使用局部实例

每个请求创建独立的局部sqlReq实例,确保每个请求的事件监听绑定到自身,避免实例覆盖。

修复后的调用代码:

var myTimer: Timer = new Timer(500, 20);

myTimer.addEventListener(TimerEvent.TIMER, onTick);
myTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimeUp);

function getName():void{
    // 使用局部变量,避免全局覆盖
    var sql:sqlReq = new sqlReq("http://mysite.php","&user=Sarah");
    sql.addEventListener(Event.COMPLETE,gotMyName);
    sql.addEventListener(IOErrorEvent.IO_ERROR,ErrorGettingMyName);
}
function getAge():void{
    // 使用局部变量,避免全局覆盖
    var sql:sqlReq = new sqlReq("http://mysite.php","&age=32");
    sql.addEventListener(Event.COMPLETE,gotMyAge);
    sql.addEventListener(IOErrorEvent.IO_ERROR,ErrorGettingMyAge);
}
function gotMyName(event:Event):void{
   var req:sqlReq = event.target as sqlReq;
   trace("获取到名称:", req.phpReqResults());
   // 清理事件监听
   req.removeEventListener(Event.COMPLETE, gotMyName);
   req.removeEventListener(IOErrorEvent.IO_ERROR, ErrorGettingMyName);
}
 function gotMyAge(event:Event):void{
   var req:sqlReq = event.target as sqlReq;
   trace("获取到年龄:", req.phpReqResults());
   // 清理事件监听
   req.removeEventListener(Event.COMPLETE, gotMyAge);
   req.removeEventListener(IOErrorEvent.IO_ERROR, ErrorGettingMyAge);
}
function ErrorGettingMyName(event:IOErrorEvent):void{
   var req:sqlReq = event.target as sqlReq;
   trace("获取名称失败:", req.phpReqResults());
   req.removeEventListener(Event.COMPLETE, gotMyName);
   req.removeEventListener(IOErrorEvent.IO_ERROR, ErrorGettingMyName);
}
function ErrorGettingMyAge(event:IOErrorEvent):void{
   var req:sqlReq = event.target as sqlReq;
   trace("获取年龄失败:", req.phpReqResults());
   req.removeEventListener(Event.COMPLETE, gotMyAge);
   req.removeEventListener(IOErrorEvent.IO_ERROR, ErrorGettingMyAge);
}

function onTick(event:TimerEvent):void{
    getName();
    getAge();
}
function onTimeUp(event:TimerEvent):void{
    myTimer.removeEventListener(TimerEvent.TIMER, onTick);
    myTimer.removeEventListener(TimerEvent.TIMER_COMPLETE, onTimeUp);
    myTimer = null;
}

2. 重构sqlReq类:简化结构,移除冗余

去掉内部的phpLoader,直接使用继承的URLLoader本身,减少状态管理的复杂度,确保每个实例的状态独立。

修复后的sqlReq类代码:

package {
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.events.*;
import flash.net.URLVariables;
import flash.net.URLRequestMethod;
import flash.net.URLLoaderDataFormat;


public class sqlReq extends URLLoader {

    private var tempPhpVars: String;
    private var totalBytes: uint;
    
    public function sqlReq(link: String, dataVars: String) {
        super();
        
        // 配置自身的参数
        dataFormat = URLLoaderDataFormat.TEXT;
        addEventListener(Event.COMPLETE, phpReqCompleted);
        addEventListener(IOErrorEvent.IO_ERROR, phpReqError);
        addEventListener(IOErrorEvent.NETWORK_ERROR, phpReqNetworkError);
        addEventListener(ProgressEvent.PROGRESS, phpReqprogress);
        
        // 创建请求并加载
        var phpFileRequest:URLRequest = new URLRequest(link);
        phpFileRequest.method = URLRequestMethod.POST;
        phpFileRequest.data = dataVars;
        load(phpFileRequest);
    }


    private function phpReqCompleted(event: Event): void {
        tempPhpVars = event.target.data;
        // 不需要额外转发事件,直接使用自身的COMPLETE事件即可
        clearEvents();
    }

    private function phpReqError(event: IOErrorEvent): void {
        tempPhpVars = "resultIs:" + event.text;
        clearEvents();
    }
    
    private function phpReqNetworkError(event: IOErrorEvent): void {
        tempPhpVars = "networkError:" + event.text;
        clearEvents();
    }
    
    private function phpReqprogress(event: ProgressEvent): void {
        totalBytes = event.bytesTotal;
    }
    
    public function phpReqResults(): String {
        return tempPhpVars;
    }
    
    public function ByteAmount(): uint {
        return totalBytes;
    }
    
    private function clearEvents(): void {
        removeEventListener(Event.COMPLETE, phpReqCompleted);
        removeEventListener(IOErrorEvent.IO_ERROR, phpReqError);
        removeEventListener(IOErrorEvent.NETWORK_ERROR, phpReqNetworkError);
        removeEventListener(ProgressEvent.PROGRESS, phpReqprogress);
    }
}
}

修复说明

  • 调用代码中使用局部变量存储每个sqlReq实例,彻底避免了全局覆盖问题,每个请求的回调都能访问到自身的实例数据。
  • 重构后的sqlReq类直接使用继承的URLLoader,移除了内部冗余的phpLoader,状态管理更清晰,每个实例的tempPhpVars独立,不会互相干扰。
  • 回调中通过event.target获取当前请求实例,确保拿到的是对应请求的结果,同时在回调后手动清理事件监听,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 09:48:16