javascript性能优化(6)

2023-05-16

响应接口

长运行脚本(500万以上)最好的办法就是避免他们。
接口最好在100毫秒响应用户输入。

  1. 用定时器让出时间片
    当一些JavaScript任务不能再100ms之内完成的时候,最好的办法就是:停止JavaScript运行,给UI线程机会进行更新,然后再继续运行JavaScript。
     var btn=document.querySelector('#btn');
        btn.onclick=function () {
            onefunc();
            setTimeout(function () {
                btn.style.color="red";
                console.log("setTimeout");
            },50)
            twofunc();
        }
        function onefunc() {
            console.log("onefunc");
        }
        function twofunc() {
            console.log("twofunc");
        }
此代码将在50毫秒之后,向UI队列插入一个JavaScript任务运行` btn.style.color=”red”; console.log(“setTimeout”);`。在那个点之前,所有其他的UI更新和JavaScript任务都在运行。注意:第二个参数指出什么时候应当将任务添加到UI队列之中,并不是说那时代码将被执行。这个任务必须等到队列中其他的任务都执行之后才能被执行。 结果`onefunc; twofunc;setTimeout;`

注意如果调用setTimeout的函数又调用了其他任务,耗时超过定时器延时,定时器代码将立即被执行,他与主调函数之间没有可察觉的延迟。

var btn=document.querySelector('#btn');
        btn.onclick=function () {
            onefunc();
            setTimeout(function () {
                btn.style.color="red";
//                console.log("setTimeout");
            },5)
            twofunc();
        }
        function onefunc() {
            console.log("onefunc");
        }
        function twofunc() {
            var str="0"
            for(var i=0;i<100000;i++){
                str+=i;
            }
            console.log(str);
        }
如上面的实例代码,twofunc运行超过了5ms,此时先打印`onefunc`;然后btn的字体变色,最后才打印str的字符串;这里有一个问题;如果没有注释掉`console.log(“setTimeout”);`这时候顺序会发生变化; 先打印`onefunc`;然后btn的字体变色,接着打印str的字符串;最后才打印`”setTimeout”;`

在任何一种情况下,创建一个定时器造成UI线程暂停,如同他从一个任务切换到下一个任务。因此,定时器代码复位所有相关的浏览器限制,,包括长运行脚本的时间。此外,调用栈也在定时器代码中复位为0。
这一特性使得定时器成为长运行JavaScript代码理想的跨浏览器解决方案。

**是否使用定时器取代循环的两个决定性因素**
  1. 此过程必须是同步处理吗?
  2. 数据必须按顺序处理吗?

    如果这两个回答都是“否”,那么代码将适用于定时器分解工作。

    一段基本的异步代码模式如下:

    function processArray(items, process, callback) {
        var todo = items.concat();
        setTimeout(function () {
            process(todo.shift());
            if (todo.length > 0) {
                setTimeout(arguments.callee,25);
            } else {
                callback(items);
            }
        }, 25);
    }
    //在函数内部,有两个特殊的对象:arguments 和 this。其中, arguments 的主要用途是保存函数参数, 但这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数。但是请注意:arguments.callee 从ES5严格模式中删除;请参照[MDN web docs](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments/callee)
    //items:待处理的数组 process:对每个项调用的处理函数 callback:处理结束时执行的回调函数
    processArray(array,function (e) {
        console.log(e);
    },function () {
        console.log("done");
    });

基本思想:创建一个原始数组的克隆,将他作为处理对象。每一次调用setTimeout()创建一个定时器处理队列中的第一项。调用todo.shift()返回他的第一项然后将他从数组中删除。此值作为参数传给process,然后检查是否还有更多的项需要处理。如果todo队列还有内容,那么就再启动一个定时器。因为下个定时器需要运行相同的代码,所以第一个参数传入arguments.callee。此值指向当前正在运行的匿名函数。如果不再有内容需要处理,将调用callback函数。

分解任务

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

    function saveDocument(id){  
         //save the document   
        writeText(id);   
        closeDocument(id);   
        //update the UI to indicate success   
        updateUI(id); 
    } 

如果函数运行事件太长,可以将他拆分一系列更小的步骤,把独立的方法放在定时器中调用。你可以将每个函数都放入一个数组中,然后使用setTimeout(arguments.callee, 25);

     function saveDocument(id){   
        var tasks = [openDocument, writeText, closeDocument, updateUI];   
        //execute the next task     
        var task = tasks.shift();     
        task(id);    
         //determine if there's more    
         if (tasks.length > 0){  
                setTimeout(arguments.callee, 25);   
          }  
     } 

上面的的方法就是:将每个方法放入任务数组,然后在每个定时器中调用一个方法,可以封装为

function multistep(steps, args, callback) {
        var tasks = steps.concat(); //clone the array
        setTimeout(function () {     //execute the next task
            var task = tasks.shift();
            task.apply(null, args || []);     //determine if there's more
            if (tasks.length > 0) {
                setTimeout(arguments.callee, 25);
            } else {
                callback();
            }
        }, 25);
    }

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

    function saveDocument(id) {
        var tasks = [openDocument, writeText, closeDocument, updateUI];
        multistep(tasks, [id], function () {
            alert("Save completed!");
        });
    }

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

限时运行代码

 function timedProcessArray(items, process, callback) {
        var todo = items.concat(); //create a clone of the original
        setTimeout(function () {
                var start = +new Date();
                do {
                    process(todo.shift());
                } while (todo.length > 0 && (+new Date() - start < 50));
                if (todo.length > 0) {
                    setTimeout(arguments.callee, 25);
                } else {
                    callback(items);
                }
            }, 25);
    }

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

定时器与性能

过度使用定时器会对性能产生负面影响。在代码使用定时器序列,同一时间只有一个定时器存在,只有当这个定时器结束时才创建一个新的定时器。以这种方式使用定时器不会带来性能问题。
当多个重复的定时器被同时创建会产生性能问题。因为只有一个 UI 线程,所有定时器竞争运行时间。
要在你的网页应用中限制高频率重复定时器的数量。同时,建一个单独的重复定时器,每次执行多个操作。

Web Workers 网页工人线程

var worker = new Worker("jsonparser.js"); //when the data is available, this event handler is called
    worker.onmessage = function (event) {   //the JSON structure is passed back
        var jsonData = event.data;   //the JSON structure is used
        evaluateData(jsonData);
    }; //pass in the large JSON string to parse
    worker.postMessage(jsonText);


    //inside of jsonparser.js //this event handler is called when JSON data is available
    self.onmessage = function (event) {   //the JSON string comes in as event.data
        var jsonText = event.data;   //parse the structure
        var jsonData = JSON.parse(jsonText);   //send back to the results
        self.postMessage(jsonData);
    };
    //注意一定要在http://localhost:3002/demo.html之类的端口地址打开,才可以。

页面使用 postMessage()将一个 JSON 字符串传给工人线程。工人线程在它的 onmessage 事件句柄中收到 这个字符串也就是 event.data,然后开始解析它。完成时所产生的 JSON 对象通过工人线程的 postMessage() 方法传回页面。然后此对象便成为页面 onmessage 事件句柄的 event.data。请记住,此工程只能在 Firefox 3.5 和更高版本中运行,而 Safari 4 和 Chrome 3 中,页面和工人线程之间只允许传递字符串。

summary

JavaScript和用户界面更新在同一个进程内运行,同一时刻只有其中一个可以运行。这意味着当JavaScript 代码正在运行时,用户界面不能响应输入,反之亦然。有效地管理 UI 线程就是要确保 JavaScript 不能运行 太长时间,以免影响用户体验。后,请牢记如下几点:

  1. JavaScript 运行时间不应该超过 100 毫秒。过长的运行时间导致 UI 更新出现可察觉的延迟,从而对整体 用户体验产生负面影响。
  2. JavaScript 运行期间,浏览器响应用户交互的行为存在差异。无论如何,JavaScript 长时间运行将导致用 户体验混乱和脱节。
  3. 定时器可用于安排代码推迟执行,它使得你可以将长运行脚本分解成一系列较小的任务。
  4. 网页工人线程是新式浏览器才支持的特性,它允许你在 UI 线程之外运行 JavaScript 代码而避免锁定 UI。
  5. 网页应用程序越复杂,积极主动地管理 UI 线程就越显得重要。没有什么 JavaScript 代码可以重要到允 许影响用户体验的程度。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

javascript性能优化(6) 的相关文章

  • MobileSafari 的正确触摸按钮行为

    MobileSafari 通常具有不正确的 HTML 按钮行为 不正确的含义 不像 iOS 原生按钮 正确的按钮行为如下 用户触摸按钮 按钮突出显示 用户将手指拖出按钮 按钮变暗 用户将手指拖回按钮 按钮突出显示 用户将手指拖出按钮并释放
  • 如何将大数据传递给网络工作者

    我正在研究网络工作人员 并将大量数据传递给网络工作人员 这需要很多时间 我想知道发送数据的有效方式 我尝试过以下代码 var worker new Worker js2 js worker postMessage buffer buffer
  • 使用 IE 11 和 AngularJS 的 2 路数据绑定问题

    我最近在使用 AngularJS 的 Web 应用程序上构建了一个功能 但在 IE 11 上遇到了一些问题 apply 将数据更改写入 DOM 由于某种原因 这种情况仅有时发生 而当我尝试调试问题时却从未发生 这使得它看起来像是一个计时问题
  • 如何在 Bootstrap 中将单选按钮设置为“选中”? [复制]

    这个问题在这里已经有答案了 我使用 Bootstrap 按钮作为单选按钮 http getbootstrap com javascript buttons http getbootstrap com javascript buttons 这
  • 禁用拉斐尔论文上的浏览器上下文菜单

    我有一篇拉斐尔论文定义为R1 Raphael 0 0 800 600 我想在本文中禁用上下文菜单 以便我可以捕获鼠标右键单击事件 我没有这样的Raphael someDiv 800 600 禁用 div 的 oncontextmenu 属性
  • 将值传递给映射函数 - CouchDB

    我想知道是否可以将值传递给 couchDB 设计文档中的映射函数 例如 在下面的代码中 可以传递用户输入的值并使用该值来运行地图函数 也许我可以传递用户UserName当他们登录时 然后根据地图功能显示视图 function doc if
  • 如何在 Chrome 中将 Set 转换为数组?

    如何将集合转换为数组 https stackoverflow com questions 20069828 how to convert set to array给出了将 Set 转换为 Array 的三个答案 目前在 Chrome 浏览器
  • Javascript CORS 图像/画布操作

    我正在尝试从另一个已配置为允许 CORS 的域检索图像 并操纵像素 然后我想显示结果并能够操纵结果 我可以在我请求的图像上使用 getImageData 和 toDataURL 所以我知道服务器部分可以工作 但是 当我尝试将图像的 src
  • 如何在 Google 地图上旋转叠加图像?

    我正在尝试将一系列叠加层放置到 Google 地图上 我正在跟随地面覆盖层的示例代码 https developers google com maps documentation javascript examples groundover
  • javascript 中的工厂模式与构造函数模式[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我看到了关于 javascript 设计模式的教程 虽然教程很好 但它给我留下了很少的问题 正如我所见 工厂和构造函数产生相同的结果
  • 如何转义 onClick 处理程序内 JavaScript 代码中的字符串?

    也许我只是想得太难了 但我在弄清楚链接的 onClick 处理程序内的某些 JavaScript 代码中的字符串上使用什么转义时遇到了问题 例子 a href Select a The and 是发生模板替换的地方 我的问题是项目名称可以包
  • 脚本不会从nodejs应用程序中的ejs文件运行

    我正在尝试使用nodejs express mysql和ejs让网页显示图表 但我显然不明白ejs javascript等是如何工作的 我需要运行一个脚本来设置图表 来自 Chart js 模块 但它不会输出任何类型的图表 我尝试过的 将
  • Lighthouse 多个 URL

    我需要对一个网站进行全面审核 但我想知道是否有任何方法可以让 Lighthouse 做到这一点 我知道他们不支持完整的站点审核或多个 URL 但我发现可以使用 bash 脚本来完成 因此 我将不胜感激对此案的任何帮助 或者您可能会推荐任何灯
  • 如何使用javascript将数据存储在xml文件中?

    我是 javascript 新手 并在我的项目中使用它 因为我需要读取 xml 文件 然后在操作后我想将更新后的值存储回 xml 文件中 我成功从 xml 文件获取值 但无法存储值返回到 xml 文件 这是我尝试过的代码
  • 从组件刷新/重新加载 ember 路由

    我有一个组件 它实际上是一个模式对话框 当我完成该对话框并按 确定 按钮时 我想留在打开该对话框的停留页面上 这并不难 但问题是该对话框更改了数据 我通过 REST 调用获取数据 因此我需要刷新已经所在的路线以反映数据更改 因为我是从组件中
  • 过滤数据表中的行

    我目前的 JQuery 插件 DataTables 工作正常 并且我在页面顶部插入了一个按钮来进一步过滤列表 我还使用了 DataTables 内置的搜索栏 我希望按钮向下过滤表格 并只显示包含特定值的行 下面是我一直在做的事情 但似乎没有
  • 汇总异常以保留模块

    我使用一个名为的汇总插件rollup plugin lit css转变 css文件转换成 javascript 模块 该插件非常简单 它本质上只是附加export default到文件 我的汇总配置使用preserveModules and
  • 如何拦截javascript中innerHTML的变化?

    我需要拦截网页内单元格内容的任何更改 以下代码显示 addEventListener 不起作用 function modifyText alert var el document getElementById mycell el inner
  • 如何从索引文件迭代多个导入的模块

    我有一个名为Polygons我在那里创建了一个index jsfile 以导出目录中的所有文件 它看起来像这样 export default as europe from europe export default as northAmer
  • 如何将国家/地区代码与电话号码分开?

    我的数据库中有很多电话号码 例如 1 123 456 7890 我要做的是将国家 地区拨号代码 在本例中为美国 加拿大的 1 与电话号码分开 我尝试创建所有国家 地区的 JSON 列表 并在加载页面时将电话号码和国家 地区代码分开 它工作正

随机推荐

  • PyQt5 使用QT Desinger 布置QChartViewer

    QChartView原来是QT公司的商用包 xff0c 后来开源了 但是相对来说文档说明少 最近想利用QT DESINGER直接拖拉拽在GUI窗体里放QChartViewer xff0c 网上参考部分资料后顺利实现 xff0c 现留作备忘
  • js拳皇特效

    js拳皇特效 效果图 很简单的特效 xff0c 运用了面向对象 xff0c 原型等简单的方法 废话不多说 xff0c 上代码 xff1a span class hljs tag lt span class hljs title script
  • 微信小程序从入门到放弃(七)

    scroll view不显示滚动条 新版本的微信小程序已经把scroll view的滚动条默认为隐藏了 xff0c 而有的业务逻辑需要把滚动条显示出来 xff1b 所以 xff1a 本人查了好久终于找到了解决的方案 xff0c 就是找到滚动
  • cookie+bootstrap-table+抽奖概率算法

    span class hljs comment 获得cookie span span class hljs function span class hljs keyword function span span class hljs tit
  • Vue新手入门-1

    基于vue2 5 9版本 数据绑定v bind 像img这样的标签 xff0c 直接在src里面写 lt img src 61 34 picimg 34 alt 61 34 34 gt xff1b 是不正确的 xff0c 这里需要用v bi
  • Vue新手入门-2

    基于vue2 5 9版本 生命周期vue1 0 created gt beforeCompile gt compiled gt ready gt attached gt detached gt beforeDestroy gt destro
  • cropper.js 裁剪图片并上传(文档翻译+demo)

    官网http fengyuanchen github io cropper 文档https github com fengyuanchen cropper blob master README md v3 x版本 引入 43 使用 span
  • Vue新手入门-3

    基于vue2 5 9版本 定义全局组件 xff08 3种写法 xff09 首先声明一个new Vue xff08 xff09 xff1b 然后在js里面编写 xff1b 最后把注册的组件放入进去即可 xff1b 写法 1 span clas
  • Vue遇到的bug-02(vue中修改了数据但视图无法更新的情况)

    基于vue2 5 9版本 vue中修改了数据但视图无法更新的情况 最近的项目需要用vue处理数组和json的数据类型发现一些问题在这里简单的列举一下 xff1b 因为 JavaScript 的限制 xff0c Vue js 不能检测到下面数
  • Vue遇到的bug-03(VUE之使用高德地图API)

    步骤一 xff1a 申请高德地图密钥 xff1b 步骤二 xff1a 在index html中添加高德地图JavaScript API接口 xff1b span class hljs tag lt span class hljs title
  • 纯css模仿微信switch开关按钮

    业务需要需要做一个微信switch开关 效果图 html样式 span class hljs tag lt span class hljs title label span gt span span class hljs tag lt sp
  • SDN实验环境的搭建UBUNTU 14LTS+MININET+RYU

    RYU是NTT主推的开源SDN 控制器项目 xff0c 采用PYTHON语言 因为工作需要 xff0c 进行了一些尝试 xff0c 现将基础环境的搭建和相关参考资料记录如下 1 系统选择UBUNTU 14 LTS xff0c 采用VMWAR
  • 移动端开发小记

    meta 在我们开发移动端的时候 xff0c 首先在head里面写入如下的代码 span class hljs tag lt span class hljs title meta span span class hljs attribute
  • less 简单用法

    less 示例 声明变量 用 64 span class token atrule span class token rule 64 man color span span class token punctuation span f0f0
  • javascript性能优化(1)

    加载 1 javascript的第一条定律 xff1a 将脚本 xff08 js xff09 放到底部 2 每一个http请求都会造成额外的性能负担 xff0c 下载一个100k的文件比下载四个25k的文件要快 xff1b 减少引用外部脚本
  • javascript性能优化(2)

    数据访问 四种基本数据类型 xff1a 1 直接量 xff1a 仅是自身不存储于特定位置 包括 xff1a 字符串 数字 布尔值 对象 数组 函数 正则表达式 xff0c 具有特殊意义的空值 xff0c 以及未定义 2 变量 xff1a v
  • javascript性能优化(3)

    DOM编程 1 文档对象模型 xff08 DOM xff09 访问的dom越多 xff0c 代码的执行速度就越慢 2 innerHtml和DOM方法 更改dom的时候 xff0c 使用innerHTML xff08 字符串拼接 xff09
  • javascript性能优化(4)

    算法和流程控制 代码整体结构是执行速度的决定因素之一 代码量少不一定运行速度快 xff0c 代码量多也不一定运行速度慢 性能损失与代码组织方式和具体问题解决办法直接相关 循环 1 减少对象成员和数组项查找的次数 span class hlj
  • javascript性能优化(5)

    字符串和正则表达式 span class hljs keyword str span 61 span class hljs string 34 a 34 span 43 span class hljs string 34 b 34 span
  • javascript性能优化(6)

    响应接口 长运行脚本 xff08 500万以上 xff09 最好的办法就是避免他们 接口最好在100毫秒响应用户输入 用定时器让出时间片 当一些JavaScript任务不能再100ms之内完成的时候 xff0c 最好的办法就是 xff1a