extJS 如何在Panel加载组件时显示遮罩层?

效果就是一个panel,是空白的,然后这个panel写一个loadPanel的方法,调用这个方法可以动态为panel加载组件,期间需要显示遮罩来提示'加载中,请稍候...',如果加载成功则除去遮罩层,并返回加载的组件,如果不成功则遮罩提示'加载失败'并返回null
我的代码如下,但只是美好的幻觉代码而已:
[code="java"]
myPanel=Ext.extend(Ext.Panel,{
loadPanel : function(config){
this.el.mask('加载中,请稍候...');
//创建组件
var c = null;
try{
c = Ext.create(config);
this.add(c);
this.doLayout();
this.el.unmask();
}
catch(err){
this.el.mask('加载失败!');
}
finally{
return c;
}
}
});
[/code]
上面的代码看起来好像挺有条理的,但有很多破绽:
1.'加载中'的提示如果不强行中断(比如在创建组件前alert中断进程),那么永远都看不见这个提示,就已经执行到create了,但是由于javascript是单线程的,所以在create的时候是假死状态,根本不可能显示出'加载中'的提示,我怀疑mask是延时执行的!
2.由于是单线程,所以除非try抛出异常,否则肉眼根本看不见遮罩层的显示,因为单线程导致浏览器假死.
3.如果使用defer延迟create的执行,那么这个方法又无法返回结果,因为javascript没有sleep等待功能,defer只是推迟几秒再执行指定的方法,只是一个回调,并不能强制线程等待,所以用defer则永远得不到create出来的c

希望大家帮忙如何实现组件加载的遮罩效果

create中[b]同步[/b]访问后台?

应避免使用同步请求,这段时间浏览器完全阻塞住,又什么事都不干,太浪费了。你可以看到Ext的API根本就不提供同步Ajax请求。

那还是之前说的,应该换回调的方式编码

JS单线程其实很好的,要知道多线程编程的复杂度…… 回调用好了,浏览器基本不会干无用功。
不过需要把任务分配好,不能有时间过长的任务一次运行,而应该用defer等拆成异步运行,浏览器始终能即时响应。

示例:
[code="js"]
loadPanel : function(opt, callback, scope){
opt.callback = function(options, success, response){
// 解析,创建,添加,布局
callback.call(scope, success, cmp);
this.el.unmask();
};
opt.scope = this;
this.el.mask();
Ext.Ajax.request(opt);
}
[/code]

这里运行占用时间最长的应该是this.doLayout,而不是create,所以可以将doLayout及unmask延后执行,这样mask能更新出来。

[code="js"]
myPanel=Ext.extend(Ext.Panel,{
loadPanel : function(config){
this.el.mask('加载中,请稍候...');
//创建组件
var c = null;
try{
c = Ext.create(config);
(function(){
this.add(c);
this.doLayout();
this.el.unmask();
}).defer(1, this);
// this.add(c);
// this.doLayout();
// this.el.unmask();
}
catch(err){
this.el.mask('加载失败!');
}
finally{
return c;
}
}
});
[/code]

或者,不用return传递,改用callback,这样能避免很多问题。比如loadPanel后续逻辑要求组件已经render完毕

其实个人认为mask操作拖慢了界面响应,异步请求时用用还不错,都是本地执行时还不如省下时间让任务快点执行完。