使用定时器处理长时间运行脚本

长时间运行的javascript任务

  对于浏览器来说,一些复杂的javascript不能在100毫秒或更短时间内完成。这时候,UI线程被长时间占用,页面UI得不到更新,对于用户来说是非常不好的体验。

这时候最理想的方法就是让出UI线程的控制权,让出控制权意味着停止执行javascript,使得UI有机会更新。然后再执行javascript。
创建一个 定时器 会造成UI线程暂停。

使用定时器处理数组

一种常见的造成长时间运行脚本的起因是耗时过长的循环。

典型的简单循环模式:
for (var i = 0 , len = items.length; i < len; i++) {
  process(items[i]);
}

是否可以用定时器取代循环的两个决定性因素:

  1. 处理过程不需要同步
  2. 数据是不需要按顺序处理

    一种基本的异步代码模式:
    var todo = items.concat(); //克隆数组
    setTimeout(function(){
      // 取得数组的下一个元素并出栈。
      process(tods.shift());
      // 如果还有需要处理的元素,创建另一个定时器
      if(todo.length > 0){
        setTimeout(argument.callee , 25)
        // argument.callee 匿名函数下调用本身。
      }else{
        callback(items);
        // 没有需要处理的元素了,执行回调函数。
      }
    },25)
    

函数封装:

/*
*处理耗时过长的异步循环。
*参数:待处理数组,处理数组的函数,回调函数
*/
function processArray(items,process,callback){
  var todo = items.concat(); //克隆数组
  setTimeout(function(){
    // 取得数组的下一个元素并出栈。
    process(tods.shift());
    // 如果还有需要处理的元素,创建另一个定时器
    if(todo.length > 0){
      setTimeout(argument.callee , 25)
      // argument.callee 匿名函数下调用本身。
    }else{
      callback(items);
    }
  },25)
}

分割任务

多任务函数导致长时间运行
function saveDocument(id){
  // 写入文档
  openDocument(id);
  writeText(id);
  closeDocument(id);
  // 更新UI
  updata(id);
}

处理思路:将每个任务函数放在一个数组中,然后使用前面提到的数组处理模式。

function saveDocument(id){
  var tasks = [openDocument,writeText,closeDocument,updataUI];
  setTimeout(function(){
    // 执行下一个任务
    var task = tasks.shift();
    task(id);
    // 检查是否还有任务
    if(task.length > 0){
      setTimeout(argument.callee , 25)
    }
  },25);
}

函数封装:

/*
*分割多任务函数的长时间运行javascript
*参数:任务步骤数组,参数,回调函数
*/
function multistep(steps,args,callback){
  var tasks = steps.concat();
  setTimeout(function(){
    // 执行下一个任务
    var task = tasks.shift();
    task.apply(null,args || []);
    // 检查是否还有任务
    if(task.length > 0){
      setTimeout(argument.callee , 25)
    }else{
      callback();
    }
  },25);
}

定时器与性能

  • 定时器可以让你的javascript整体代码性能带来优化,但过度使用也会造成负面影响。本篇博客代码使用的了定时器序列,同一时间只有一个定时器存在,只有当这个定时器结束时才会新创建一个。
  • 当多个重复的定时器同时创建往往会出现性能问题,因为只有一个UI线程,而所有的定时器都在争夺运行时间。在使用定时器优化性能时,注意避免这种情况。