详解JavaScript之分解任务

下面来看,JavaScript分解任务,供参考。

我们通常将一个任务分解成一系列子任务。如果一个函数运行时间太长,那么查看它是否可以分解成一系列能够短时间完成的较小的函数。可将一行代码简单地看作一个原子任务,多行代码组合在一起构成一个独立任务。某些函数可基于函数调用进行拆分。例如:

 
 
 
  1. function saveDocument(id){
  2. openDocument(id)
  3. writeText(id);
  4. closeDocument(id);
  5. updateUI(id);
  6. }

如果函数运行时间太长,它可以拆分成一系列更小的步骤,把独立方法放在定时器中调用。你可以将每个函数都放入一个数组,然后使用前一节中提到的数组处理模式:

 
 
 
  1. function saveDocument(id){
  2. var tasks = [openDocument, writeText, closeDocument, updateUI];
  3. setTimeout(function(){
  4. var task = tasks.shift();
  5. task(id);
  6. if (tasks.length > 0){
  7. setTimeout(arguments.callee, 25);
  8. }
  9. }, 25);
  10. }

这个版本将每个方法放入任务数组,然后在每个定时器中调用一个方法。从根本上说,现在它成为数组处理模式,只有一点不同:处理函数就包含在数组项中。正如前面一节所讨论的,此模式也可封装重用:

 
 
 
  1. function multistep(steps, args, callback){
  2. var tasks = steps.concat();
  3. setTimeout(function(){
  4. var task = tasks.shift();
  5. task.apply(null, args || []);
  6. if (tasks.length > 0){
  7. setTimeout(arguments.callee, 25);
  8. } else {
  9. callback();
  10. }
  11. }, 25);
  12. }

multistep()函数接收三个参数:用于执行的函数数组,为每个函数提供参数的参数数组,当处理结束时调用的回调函数。函数用法如下:

 
 
 
  1. function saveDocument(id){
  2. var tasks = [openDocument, writeText, closeDocument, updateUI];
  3. multistep(tasks, [id], function(){
  4. alert("Save completed!");
  5. });
  6. }

注意传给multistep()的第二个参数必须是数组,它创建时只包含一个id。正如数组处理那样,使用此函数的前提条件是:任务可以异步处理而不影响用户体验或导致依赖代码出错。

(一)限时运行代码

有时每次只执行一个任务效率不高。考虑这样一种情况:处理一个拥有1'000个项的数组,每处理一个项需要1毫秒。如果每个定时器中处理一个项,在两次处理之间间隔25毫秒,那么处理此数组的总时间是(25 + 1) × 1'000 = 26'000 秒,也就是26 秒。如果每批处理50个,每批之间间隔25毫秒会怎么样呢?整个处理过程变成(1'000 / 50) × 25 + 1'000 = 1'500毫秒,也就是1.5秒,而且用户也不会察觉界面阻塞,因为最长的脚本运行只持续了50毫秒。通常批量处理比每次处理一个更快。

如果你记住JavaScript可连续运行的最大时间是100毫秒,那么你可以优化先前的模式。我的建议是将这个数字削减一半,不要让任何JavaScript代码持续运行超过50毫秒,只是为了确保代码永远不会影响用户体验。

可通过原生的Date 对象跟踪代码的运行时间。这是大多数JavaScript分析工具所采用的工作方式:

 
 
 
  1. var start = +new Date(),
  2. stop;
  3. someLongProcess();
  4. stop = +new Date();
  5. if(stop-start < 50){
  6. alert("Just about right.");
  7. } else {
  8. alert("Taking too long.");
  9. }

由于每个新创建的Data 对象以当前系统时间初始化,你可以周期性地创建新Data对象并比较它们的值,以获取代码运行时间。加号(+)将Data 对象转换为一个数字,在后续的数学运算中就不必再转换了。这一技术也可用于优化以前的定时器模板。
processArray()方法通过一个时间检测机制,可在每个定时器中执行多次处理:

 
 
 
  1. function timedProcessArray(items, process, callback){
  2. var todo = items.concat();
  3. setTimeout(function(){
  4. var start = +new Date();
  5. do {
  6. process(todo.shift());
  7. } while (todo.length > 0 && (+new Date() – start < 50));
  8. if (todo.length > 0){
  9. setTimeout(arguments.callee, 25);
  10. } else {
  11. callback(items);
  12. }
  13. }, 25);
  14. }

此函数中添加了一个do-while循环,它在每个数组项处理之后检测时间。定时器函数运行时数组中存放了至少一个项,所以后测试循环比前测试更合理。在Firefox 3中,如果process()是一个空函数,处理一个1'000个项的数组需要38 – 34毫秒;原始的processArray()函数处理同一个数组需要超过25'000毫秒。这就是定时任务的作用,避免将任务分解成过于碎小的片断。

(二)定时器与性能

定时器使你的JavaScript代码整体性能表现出巨大差异,但过度使用它们会对性能产生负面影响。使用定时器序列,同一时间只有一个定时器存在,只有当这个定时器结束时才创建一个新的定时器。以这种方式使用定时器不会带来性能问题。

当多个重复的定时器被同时创建会产生性能问题。因为只有一个UI线程,所有定时器竞争运行时间。Google Mobile的Neil Thomas 将此问题作为测量性能的方法进行研究,针对iPhone和Android上运行的移动Gmail程序。

Thomas发现低频率的重复定时器——间隔在1 秒或1 秒以上——几乎不影响整个网页应用的响应。这种情况下定时器延迟远超过使UI 线程产生瓶颈的值,因此可安全地重复使用。当多个重复定时器使用更高的频率(间隔在100到200毫秒之间),Thomas发现移动Gmail程序明显变慢,反应较差。

Thomas研究的言外之意是,要在你的网页应用中限制高频率重复定时器的数量。同时,Thomas建议创建一个单独的重复定时器,每次执行多个操作。

原文地址: http://www.yiiyaa.net/1223

当前标题:详解JavaScript之分解任务
分享链接:http://www.shufengxianlan.com/qtweb/news1/410351.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联