JS魔法堂:初探传说中的setImmediate函数

一、前言                              

   由于JavaScript程序为单线程,因此在执行长时间的操作时(如循环和递归操作)到导致UI线程长期被阻塞,无法响应用户操作请求(如点击按钮等),让用户体验大打折扣。于是想到将一个长时间操作切片成N个小操作并异步执行,例如jsDeferred中的 Deferred.repeat函数 就提供这样的解决办法,其实质就是通过 setTimeout事件 或 script元素 的 onerror/onload事件 来异步调用这些小操作,从而尽早释放UI线程。

   从IE10开始引入了setImmediate接口来代替setTimeout来完成上述功能,下面将记录该接口的资讯,由于内容会涉及到event loop、调用栈等知识,而我对相关内容了解仍不全面,因此下面的内容若有纰漏请各位指正,谢谢!

 

二、同步和异步调用                        

  由于JavaScript是通过异步调用来尽早释放UI线程,因此我们先要了解同步和异步执行的具体含义:

  任务的执行实质上分为两步:①.执行,②.获取执行结果。

  同步执行:执行后等待直到获取执行结果;

  异步执行:执行后不等待,而是通过一系列手段(轮询、事件监听和event loop等)获取执行结果,而在执行后和获取结果前的那段时间可以介入其他任务操作。  

 

二、setTimeout(handler, 0)的问题                

 
由于setTimeout存在时间精度,因此setTimeout(handler,0)中setTimeout事件插入事件队列的延时必定大于0ms,
而handler的执行延时则更大了。具体为IE5~8和不插电源的IE9的时间精度为15.6ms,插电源的IE9和其他浏览器则为4ms。

  经微软和Chrome团队实验所得降低时间精度将会大大缩短笔记本的续航时间,也是就说更耗电,因此即使浏览器厂商有能力缩短时间精度,但基于多方面的考虑,依然保持上述的精度值。

    

三、setImmediate接口                     

  对于通过异步执行的手段对任务切片,由于UI线程得到释放从而提高用户体验,但相对于采用同步执行,整体的任务执行时间较被拉长,因此我们希望切片的小操作越快执行越好。而setImmediate接口则是为此而生的。

   setImmediate(handler) 并不像 setTimeout(handler, 0) 由event loop检测系统时间是否到点然后向事件队列插入一个事件,然后调用事件的回调方法handler。而是监控UI线程的调用栈,一旦调用栈为空则将handler压栈。

   理论上通过setImmediate执行异步调用的延时一定比通过setTimeout的短,但事实又是如何呢?

   我在IE11上操作测试,setTimout的延时为4ms左右,而setImmediate的延时为2ms左右。

   注意:

     1. 通过setImmediate的异步调用的延时不是0ms哦!

     2. 而且有时候setImmediate的延时比setTimeout的多1~2ms哦!

     3. 而且setImmediate和setTimeout的延时均比img元素onerror事件的延时长哦!

  推测:

     1. 对于setImmediate的延时有时比setTimeout的要长,由于setImmediate要先监控调用栈,若调用栈为空才压栈,那么在压栈之前event loop已经将setTimeout事件的回调函数压栈了。

     2. 对于两者均比img元素onerror事件的长,由于设置img.src后马上发起请求(不一定为网络IO)当加载失败时onerror事件则会比setTimeout事件先加入事件队列。

 

四、总结                            

  本文内容纰漏之处请各位指正,谢谢!

时间: 2024-10-23 06:04:47

JS魔法堂:初探传说中的setImmediate函数的相关文章

JS魔法堂:判断节点位置关系

一.前言   在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅.   二.祖孙关系 html <div id="ancestor"> <div id="parent"> <div id="son">son</div> </div> </div>

JS魔法堂:精确判断IE的文档模式by特征嗅探

一.前言   苦逼的前端攻城狮都深受浏览器兼容之苦,再完成每一项功能前都要左顾右盼,生怕浏览器不支持某个API,生怕原生API内含臭虫因此判断浏览器类型和版本号成了不可绕过的一道关卡,而特征嗅探是继浏览器探测后另一利器处理上述问题.   二.何为特征嗅探 从前我们都是通过对navigator.userAgent或navigator.appVersion两个属性值进行特定字符串匹配和萃取来区 分浏览器类型和获取版本号的.但随着IE8提供可选的文档兼容性模式设置和各种加壳浏览器的出现,导致无法通过n

JS魔法堂:属性、特性,傻傻分不清楚

或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById("dummy"); el.hello = "test"; console.log(el.getAttribute("hello")); // IE67下输出test,其他浏览器输出null   "搞毛啊?",苦逼的Jser对着浏览器大呼一声.然后就用下面蹩脚的方式草草处理掉了. function getAttr(el, p

JS魔法堂:追忆那些原始的选择器

一.前言                                                                                                      首先这里说的原始选择器是指除 querySelector . querySelectorAll 外的其他选择器.从前我只使用 getElementById 获取元素并没有觉得有什么问题,但随着参与项目的前端规模逐步扩大,踩的坑就越来越多,于是将踩过的和学习过的经验教训记录在这里,供以后好

JS魔法堂:那些困扰你的DOM集合类型

一.前言 大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②. 抛namedItem is undefined的异常 var nodes = document.getElementsByName('dummyName'); var node = nodes.namedItem('id'); 答案是两种都有可能哦!document.getElementsByName在Chrome和FF30.0中返回NodeList(木有namedItem方法的),在IE

JS魔法堂:IMG元素加载行为详解

一.前言   在<JS魔法堂:jsDeferred源码剖析>中我们了解到img元素加载失败可以作为函数异步执行的优化方案,本文打算对img元素的加载行为进行更深入的探讨.   二.资源加载的相关属性和事件   资源加载首先当然是确定资源位置的 src属性 .随之就是资源加载成功与否的 onload事件 和 onerror事件 ,对于IE5~10来说还多了一个 onreadystatechage事件 和与该事件相关联的 readyState属性 和 complete属性 .    onload事

JS魔法堂:不完全国际化&amp;本地化手册 之 实战篇

前言  最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已.趁着这个机会好好学习整理一下,为后面的技术选型做准备.  本篇将于大家一起挽起袖子撸代码:) 如何获取Language tag?  在实现本地化处理前,我们起码先要获取Language tag吧?那么获取方式分为两类 1.直接获取浏览器的Language tag信息  一般来说浏览器语言的版本标示着用户所属或

JS魔法堂:jsDeferred源码剖析

一.前言    最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发 起的jsDeferred项目(<JavaScript框架设计>提供该资讯,再次感谢),追本溯源地了解jsDeferred是十分有必要的,并且当你 看过官网(http://cho45.stfuawsc.com/jsdeferred/)的新手引导后就会有种不好好学学就太可惜的感觉了,而只看 API和使用指南是无法满足我对它的好奇心的,通过解读源码读透它的设计思

JS魔法堂:获取当前脚本文件的绝对路径

原文:JS魔法堂:获取当前脚本文件的绝对路径 一.前言   当写模块加载器时,获取当前脚本文件的绝对路径作为基础路径是必不可少的一步,下面我们一起来探讨一下这个问题吧!   二.各大浏览器的实现方式   [a]. Chrome和FF   超简单的一句足矣! var getCurrAbsPath = function(){ return document.currentScript.src; }; 这里利用了对象 document.currentScript ,它返回的是当前执行的script元素