服务报价 | 域名主机 | 网络营销 | 软件工具| [加入收藏]
 热线电话: #
当前位置: 主页 > 开发教程 > HTML/CSS教程 >

JavaScript 如何实现后台计划任务

时间:2016-01-15 10:30来源:未知 作者:最模板 点击:
即使忘了 JavaScript 的一切知识,也不会忘记:它是阻塞的。 想象一下,你的浏览器里住着一个魔法小精灵,负责浏览器的正常运转。不论渲染 HTML,响应菜单命令,屏幕渲染,处理鼠标

即使忘了 JavaScript 的一切知识,也不会忘记:它是阻塞的。

想象一下,你的浏览器里住着一个魔法小精灵,负责浏览器的正常运转。不论渲染 HTML,响应菜单命令,屏幕渲染,处理鼠标点击,或者执行 JavaScript 函数,所有事情都归一个小精灵处理。它哪忙得过来,一次只能处理一件事情。如果同时丢给它一堆任务,它会列一个长长的待办列表,按顺序完成它们。

人们常常希望初始化组件和事件处理的 JavaScript 可以尽快被执行。可是,有些不太重要的后台任务不会直接影响用户体验,比如:

  • 记录统计数据
  • 发送数据到社交网络(或添加‘分享’按钮)
  • 预加载内容
  • 预处理或预渲染 HTML

他们对时序要求不严格,但是为了让页面仍然响应,直到用户滚动页面或者与内容交互时才被执行。

选择之一是 Web Workers ,它可以在独立的线程同时执行代码。用于预加载和预处理再好不过,但是你没有权限直接访问或更新 DOM。你可以在自己的代码中避开这点,但是无法保证第三方脚本比如 Google Analytics 永远不需要这个。

另一个选择是 setTimeout ,比如 setTimeout(doSomething, 1); 。一旦其它的立即执行任务执行完毕,浏览器将执行 doSomething() 函数。实际上,它被放到了待办列表的底部。不幸的是,函数将被调用,而不顾处理需求。

#requestIdleCallback

requestIdleCallback 是新API,当浏览器稍作喘息的时候,用来执行不太重要的后台计划任务。 难免让人想起 requestAnimationFrame ,在下次重绘之前,执行函数更新动画。 想了解更多戳这里: 使用 requestAnimationFrame 做简单的动画 。

requestIdleCallback 特性监测:

if ('requestIdleCallback' in window) {
  // requestIdleCallback supported
  requestIdleCallback(backgroundTask);
}
else {
  // no support - do something else
  setTimeout(backgroundTask1, 1);
  setTimeout(backgroundTask2, 1);
  setTimeout(backgroundTask3, 1);
}

也可以指定配置参数对象,比如 timeout,

requestIdleCallback(backgroundTask, { timeout: 3000; });

确保函数在3秒之内调用,不管浏览器是否空闲。

deadline 对象传入以下参数时, requestIdleCallback 仅执行一次回调:

  • didTimeout —— 如果可选的 timeout 触发,则设置为 true
  • timeRemaining() —— 函数返回执行任务剩余的毫秒数 
    timeRemaining() 最多分配50ms用于任务的执行,超过这个限制,也不会停止任务,但是,最好重新调用 requestIdleCallback 安排进一步的处理。

我们来创建一个简单的例子,让几个任务按序执行。任务的函数引用储存在数组中:

//待执行的函数数组
var task = [
    background1,
    background2,
    background3
];

if ('requestIdleCallback' in window) {
  //支持 requestIdleCallback
  requestIdleCallback(backgroundTask);
}
else {
  //不支持 —— 立刻执行所有任务
  while (task.length) {
    setTimeout(task.shift(), 1);
  }
}

//requestIdleCallback 回调函数
function backgroundTask(deadline) {

  //如果存在,执行下一个任务
  while (deadline.timeRemaining() > 0 && task.length > 0) {
    task.shift()();
  }

  //需要的话,安排进一步任务
  if (task.length > 0) {
    requestIdleCallback(backgroundTask);
  }
}

#一次 requestIdleCallback 之间不应该做什么?

Paul Lewis 在 他的文章 中提到,一次 requestIdleCallback 执行的任务应该切成小块。它不适用于不可预知时间的情况(比如操作 DOM,使用 requestAnimationFrame 回调更好些)。resolving(或者 rejecting)Promises 时也要谨慎,即使没有更多的剩余时间,空闲回调完成之后,回调函数也将立即执行。

#requestIdleCallback 浏览器支持情况

requestIdleCallback 是试验性特性,规范仍不稳定,碰到 API 变更时不足为奇。Chrome 47 已支持… 2015年结束前应该可用了。Opera 应该会紧跟其后。Microsoft 和 Mozilla 都在考虑 API 是否应该支持 Promises 。Apple 像往常一样不鸟。

Paul Lewis(上文提到的)写了一个简单的 requestIdleCallback shim ,它可以模拟浏览器的空闲监测行为,但不是一个 polyfill( shim 和 polyfill 的区别 )。

requestIdleCallback shim代码如下:

/*!
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
 
/*
 * @see https://developers.google.com/web/updates/2015/08/using-requestidlecallback
 */
window.requestIdleCallback = window.requestIdleCallback ||
  function (cb) {
    var start = Date.now();
    return setTimeout(function () {
      cb({ 
        didTimeout: false,
        timeRemaining: function () {
          return Math.max(0, 50 - (Date.now() - start));
        }
      });
    }, 1);
  }

window.cancelIdleCallback = window.cancelIdleCallback ||
  function (id) {
    clearTimeout(id);
  }

(责任编辑:最模板)
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
栏目列表
热点内容