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
相关产品推荐
相关产品推荐

