金沙国际官网_金沙国际平台登录

因为这个金沙国际官网_金沙国际平台登录网站与很多的大型澳门赌场都有合作,金沙国际官网_金沙国际平台登录尽职尽责,高效执行,保持好奇心,不断学习,追求卓越,点击进入金沙国际官网_金沙国际平台登录马上体验吧,所以现在也正式地开始了营业。

您的位置:金沙国际官网 > web前端 > 浏览器大战能消停吗,函数的性能

浏览器大战能消停吗,函数的性能

发布时间:2019-11-03 01:48编辑:web前端浏览(151)

    从setTimeout/setInterval看JS线程

    2018/04/19 · JavaScript · setInterval, settimeout

    原文出处: PalmerYe   

    最近项目中遇到了一个场景,其实很常见,就是定时获取接口刷新数据。那么问题来了,假设我设置的定时时间为1s,而数据接口返回大于1s,应该用同步阻塞还是异步?我们先整理下js中定时器的相关知识,再来看这个问题。

    初识setTimeout 与 setInterval

    先来简单认识,后面我们试试用setTimeout 实现 setInterval 的功能

    setTimeout 延迟一段时间执行一次 (Only one)

    setTimeout(function, milliseconds, param1, param2, ...) clearTimeout() // 阻止定时器运行 e.g. setTimeout(function(){ alert("Hello"); }, 3000); // 3s后弹出

    1
    2
    3
    4
    5
    setTimeout(function, milliseconds, param1, param2, ...)
    clearTimeout() // 阻止定时器运行
     
    e.g.
    setTimeout(function(){ alert("Hello"); }, 3000); // 3s后弹出

    setInterval 每隔一段时间执行一次 (Many times)

    setInterval(function, milliseconds, param1, param2, ...) e.g. setInterval(function(){ alert("Hello"); }, 3000); // 每隔3s弹出

    1
    2
    3
    4
    setInterval(function, milliseconds, param1, param2, ...)
     
    e.g.
    setInterval(function(){ alert("Hello"); }, 3000); // 每隔3s弹出

    setTimeout和setInterval的延时最小间隔是4ms(W3C在HTML标准中规定);在JavaScript中没有任何代码是立刻执行的,但一旦进程空闲就尽快执行。这意味着无论是setTimeout还是setInterval,所设置的时间都只是n毫秒被添加到队列中,而不是过n毫秒后立即执行。

    进程与线程,傻傻分不清楚

    为了讲清楚这两个抽象的概念,我们借用阮大大借用的比喻,先来模拟一个场景:

    这里有一个大型工厂
    工厂里有若干车间,每次只能有一个车间在作业
    每个车间里有若干房间,有若干工人在流水线作业

    那么:

    一个工厂对应的就是计算机的一个CPU,平时讲的多核就代表多个工厂
    每个工厂里的车间,就是进程,意味着同一时刻一个CPU只运行一个进程,其余进程在怠工
    这个运行的车间(进程)里的工人,就是线程,可以有多个工人(线程)协同完成一个任务
    车间(进程)里的房间,代表内存。

    再深入点:

    车间(进程)里工人可以随意在多个房间(内存)之间走动,意味着一个进程里,多个线程可以共享内存
    部分房间(内存)有限,只允许一个工人(线程)使用,此时其他工人(线程)要等待
    房间里有工人进去后上锁,其他工人需要等房间(内存)里的工人(线程)开锁出来后,才能才进去,这就是互斥锁(Mutual exclusion,缩写 Mutex)
    有些房间只能容纳部分的人,意味着部分内存只能给有限的线程

    再再深入:

    如果同时有多个车间作业,就是多进程
    如果一个车间里有多个工人协同作业,就是多线程
    当然不同车间之间的工人也可以有相互协作,就需要协调机制

    JavaScript 单线程

    总所周知,JavaScript 这门语言的核心特征,就是单线程(是指在JS引擎中负责解释和执行JavaScript代码的线程只有一个)。这和 JavaScript 最初设计是作为一门 GUI 编程语言有关,最初用于浏览器端,单一线程控制 GUI 是很普遍的做法。但这里特别要划个重点,虽然JavaScript是单线程,但浏览器是多线程的!!!例如Webkit或是Gecko引擎,可能有javascript引擎线程、界面渲染线程、浏览器事件触发线程、Http请求线程,读写文件的线程(例如在Node.js中)。ps:可能要总结一篇浏览器渲染的文章了。

    HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

    同步与异步,傻傻分不清楚

    之前阮大大写了一篇《JavaScript 运行机制详解:再谈Event Loop》,然后被朴灵评注了,特别是同步异步的理解上,两位大牛有很大的歧义。

    同步(synchronous):假如一个函数返回时,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果),这就是同步函数。

    e.g. alert('马上能看到我拉'); console.log('也能马上看到我哦');

    1
    2
    3
    e.g.
    alert('马上能看到我拉');
    console.log('也能马上看到我哦');

    异步(asynchronous):假如一个函数返回时,调用者不能得到预期结果,需要通过一定手段才能获得,这就是异步函数。

    e.g. setTimeout(function() { // 过一段时间才能执行我哦 }, 1000);

    1
    2
    3
    4
    e.g.
    setTimeout(function() {
        // 过一段时间才能执行我哦
    }, 1000);

    异步构成要素

    一个异步过程通常是这样的:主线程发起一个异步请求,相应的工作线程(比如浏览器的其他线程)接收请求并告知主线程已收到(异步函数返回);主线程可以继续执行后面的代码,同时工作线程执行异步任务;工作线程完成工作后,通知主线程;主线程收到通知后,执行一定的动作(调用回调函数)。

    发起(注册)函数 – 发起异步过程
    回调函数 – 处理结果

    e.g. setTimeout(fn, 1000); // setTimeout就是异步过程的发起函数,fn是回调函数

    1
    2
    3
    e.g.
    setTimeout(fn, 1000);
    // setTimeout就是异步过程的发起函数,fn是回调函数

    通信机制

    异步过程的通信机制:工作线程将消息放到消息队列,主线程通过事件循环过程去取消息。

    消息队列 Message Queue

    一个先进先出的队列,存放各类消息。

    事件循环 Event Loop

    主线程(js线程)只会做一件事,就是从消息队列里面取消息、执行消息,再取消息、再执行。消息队列为空时,就会等待直到消息队列变成非空。只有当前的消息执行结束,才会去取下一个消息。这种机制就叫做事件循环机制Event Loop,取一个消息并执行的过程叫做一次循环。图片 1

    工作线程是生产者,主线程是消费者。工作线程执行异步任务,执行完成后把对应的回调函数封装成一条消息放到消息队列中;主线程不断地从消息队列中取消息并执行,当消息队列空时主线程阻塞,直到消息队列再次非空。

    setTimeout(function, 0) 发生了什么

    其实到这儿,应该能很好解释setTimeout(function, 0) 这个常用的“奇技淫巧”了。很简单,就是为了将function里的任务异步执行,0不代表立即执行,而是将任务推到消息队列的最后,再由主线程的事件循环去调用它执行。

    HTML5 中规定setTimeout 的最小时间不是0ms,而是4ms。

    setInterval 缺点

    再次强调,定时器指定的时间间隔,表示的是何时将定时器的代码添加到消息队列,而不是何时执行代码。所以真正何时执行代码的时间是不能保证的,取决于何时被主线程的事件循环取到,并执行。

    setInterval(function, N)

    1
    setInterval(function, N)

    那么显而易见,上面这段代码意味着,每隔N秒把function事件推到消息队列中,什么时候执行?母鸡啊!图片 2

    上图可见,setInterval每隔100ms往队列中添加一个事件;100ms后,添加T1定时器代码至队列中,主线程中还有任务在执行,所以等待,some event执行结束后执行T1定时器代码;又过了100ms,T2定时器被添加到队列中,主线程还在执行T1代码,所以等待;又过了100ms,理论上又要往队列里推一个定时器代码,但由于此时T2还在队列中,所以T3不会被添加,结果就是此时被跳过;这里我们可以看到,T1定时器执行结束后马上执行了T2代码,所以并没有达到定时器的效果。

    综上所述,setInterval有两个缺点:

    使用setInterval时,某些间隔会被跳过;
    可能多个定时器会连续执行;

    链式setTimeout

    setTimeout(function () { // 任务 setTimeout(arguments.callee, interval); }, interval)

    1
    2
    3
    4
    setTimeout(function () {
        // 任务
        setTimeout(arguments.callee, interval);
    }, interval)

    警告:在严格模式下,第5版 ECMAScript (ES5) 禁止使用 arguments.callee()。当一个函数必须调用自身的时候, 避免使用 arguments.callee(), 通过要么给函数表达式一个名字,要么使用一个函数声明.

    上述函数每次执行的时候都会创建一个新的定时器,第二个setTimeout使用了arguments.callee()获取当前函数的引用,并且为其设置另一个定时器。好处:

    在前一个定时器执行完前,不会向队列插入新的定时器(解决缺点一)
    保证定时器间隔(解决缺点二)

    So…

    回顾最开始的业务场景的问题,用同步阻塞还是异步,答案已经出来了…

    PS:其实还有macrotask与microtask等知识点没有提到,总结了那么多,其实JavaScript深入下去还有很多,任重而道远呀。

     

    1 赞 收藏 评论

    图片 3

    测试 JavaScript 函数的性能

    2017/08/08 · JavaScript · 函数, 时间

    本文由 伯乐在线 - Wing 翻译,周进林 校稿。未经许可,禁止转载!
    英文出处:Peter Bengtsson。欢迎加入翻译组。

    在软件中,性能一直扮演着重要的角色。在Web应用中,性能变得更加重要,因为如果页面速度很慢的话,用户就会很容易转去访问我们的竞争对手的网站。作为专业的web开发人员,我们必须要考虑这个问题。有很多“古老”的关于性能优化的最佳实践在今天依然可行,例如最小化请求数目,使用CDN以及不编写阻塞页面渲染的代码。然而,随着越来越多的web应用都在使用JavaScript,确保我们的代码运行的很快就变得很重要。

    假设你有一个正在工作的函数,但是你怀疑它运行得没有期望的那样快,并且你有一个改善它性能的计划。那怎么去证明这个假设呢?在今天,有什么最佳实践可以用来测试JavaScript函数的性能呢?一般来说,完成这个任务的最佳方式是使用内置的performance.now()函数,来衡量函数运行前和运行后的时间。

    在这篇文章中,我们会讨论如何衡量代码运行时间,以及有哪些技术可以避免一些常见的“陷阱”。

    HTML5标准制定完成,浏览器大战能消停吗?

    2014/10/30 · HTML5 · HTML5

    原文出处: 虎嗅网   

    昨天,万维网联盟(W3C)宣布,经过将近8年的艰辛努力,HTML5标准规范终于最终制定完成并已公开发布。

    狭义上,HTML5是HTML的第五个版本。HTML的全称是超文本标记语言(HyperText Markup Language),由万维网的发明者蒂姆·伯纳斯·李设计,是为创建网页而设计的一种标记语言。HTML利用标签来描述内容的语义,使计算机能够通过识别标签来正确处理内容。

    图片 4

    广义上,HTML5是HTML5、CSS3、Javascript 2.0的统称,因为对于现在的互联网开发而言,这三者是密不可分的。HTML用于描述内容,CSS用于定义样式,Javascript用于实现功能。

    HTML是互联网的基石,目前互联网上所有的网页都是用HTML写成的。但是HTML标准的演化速度却远远跟不上互联网的发展。事实上,上一个HTML标准HTML 4.01发布于1999年12月24日,已经严重阻碍了互联网的发展。

    2004年,由Firefox、Opera、Apple、Google四大浏览器厂商组成的网页超文本技术工作小组(Web Hypertext Application Technology Working Group),即WHATWG,宣布制定下一代HTML标准,即HTML5。而当时的万维网联盟(W3C)正在发展在XML和HTML基础上设计的XHTML。

    于是,W3C和浏览器厂商的第一次大战开始。互联网的未来究竟是由标准组织W3C决定还是由浏览器厂商决定?这场大战的决定性因素在于开发者们站在哪一边。结果很明显,开发者们当然会站在浏览器那边,毕竟浏览器是普通用户接触互联网的唯一途径。W3C于2007年接纳了WHATWG的HTML5草案,并成立了新的HTML工作团队。

    然而,在2012年,W3C和WHATWG再度分道扬镳。而两者的分歧在于WHATWG 集中于演进“living”标准,而 W3C 坚持使用传统的数字编号系统定义静态的“snapshots”。 WHATWG希望构建互联网的最后一个标准,即一个随着互联网发展不断更新的HTML5标准。他们认为W3C的HTML5标准一旦制定完成,即便出现错误也无法修正。而且他们认为W3C的标准制定模式太过复杂,每一代标准的制定时间过长,不符合互联网的发展速度。

    所以,HTML5现在有两个标准,一个由W3C制定,一个由WHATWG制定。这会导致W3C和浏览器厂商的第二次大战吗?

    当然不会,对于浏览器厂商来说,赢得浏览器之战比HTML5标准更重要。自从Google的Chrome重新掀起浏览器间的大战之后,每一家浏览器都在根据自己的情况支持HTML5标准,每一家浏览器的广告都在吹嘘自己对HTML5标准的支持。

    所以,HTML5的标准已经成为了既成事实,W3C的HTML5标准只是对这个既成事实的官方认证而已。

    那么,既然有了HTML5的官方标准,浏览器大战总该消停了吧。事实上,这场大战依然在延续,而开发者们依然需要为各大浏览器适配网页。

    举例来说,HTML5标准设计了<video>标签,使得浏览器可以不借助Flash直接播放视频文件。但是,HTML5标准却没有规定浏览器支持的视频文件格式。现在,Firefox主推Ogg,Chrome主推WebM,Safari主推H.264。也就是说,开发者如果要使用<video>标签,需要准备多种格式的视频文件。好消息是现在似乎H.264占据了上风。

    一次编写,到处运行(Write once, Run anywhere)是每一个程序员的梦想。当年的Java没有做到,原本程序员们指望Web标准能够做到。然而事实上是,只要浏览器大战没有消停,HTML5也做不到。

    赞 收藏 评论

    图片 5

    Performance.now()

    高分辨率时间API提供了一个名为now()的函数,它返回一个DOMHighResTimeStamp对象,这是一个浮点数值,以毫秒级别(精确到千分之一毫秒)显示当前时间。单独这个数值并不会为你的分析带来多少价值,但是两个这样的数值的差值,就可以精确描述过去了多少时间。

    这个函数除了比内置的Date对象更加精确以外,它还是“单调”的,简单说,这意味着它不会受操作系统(例如,你笔记本上的操作系统)周期性修改系统时间影响。更简单的说,定义两个Date实例,计算它们的差值,并不代表过去了多少时间。

    “单调性”的数学定义是“(一个函数或者数值)以从不减少或者从不增加的方式改变”。

    我们可以从另外一种途径来解释它,即想象使用它来在一年中让时钟向前或者向后改变。例如,当你所在国家的时钟都同意略过一个小时,以便最大化利用白天的时间。如果你在时钟修改之前创建了一个Date实例,然后在修改之后创建了另外一个,那么查看这两个实例的差值,看上去可能像“1小时零3秒又123毫秒”。而使用两个performance.now()实例,差值会是“3秒又123毫秒456789之一毫秒”。

    在这一节中,我不会涉及这个API的过多细节。如果你想学习更多相关知识或查看更多如何使用它的示例,我建议你阅读这篇文章:Discovering the High Resolution Time API。

    既然你知道高分辨率时间API是什么以及如何使用它,那么让我们继续深入看一下它有哪些潜在的缺点。但是在此之前,我们定义一个名为makeHash()的函数,在这篇文章剩余的部分,我们会使用它。

    JavaScript

    function makeHash(source) {  var hash = 0;  if (source.length === 0) return hash;  for (var i = 0; i < source.length; i++) {    var char = source.charCodeAt(i);    hash = ((hash<<5)-hash)+char;    hash = hash & hash; // Convert to 32bit integer  }  return hash; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function makeHash(source) {
     var hash = 0;
     if (source.length === 0) return hash;
     for (var i = 0; i < source.length; i++) {
       var char = source.charCodeAt(i);
       hash = ((hash<<5)-hash)+char;
       hash = hash & hash; // Convert to 32bit integer
     }
     return hash;
    }

    我们可以通过下面的代码来衡量这个函数的执行效率:

    JavaScript

    var t0 = performance.now(); var result = makeHash('Peter'); var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);

    1
    2
    3
    4
    var t0 = performance.now();
    var result = makeHash('Peter');
    var t1 = performance.now();
    console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);

    如果你在浏览器中运行这些代码,你应该看到类似下面的输出:

    JavaScript

    Took 0.2730 milliseconds to generate: 77005292

    1
    Took 0.2730 milliseconds to generate: 77005292

    这段代码的在线演示如下所示:

    记住这个示例后,让我们开始下面的讨论。

    缺陷1 – 意外衡量不重要的事情

    在上面的示例中,你可以注意到,我们在两次调用performance.now()中间只调用了makeHash()函数,然后将它的值赋给result变量。这给我们提供了函数的执行时间,而没有其他的干扰。我们也可以按照下面的方式来衡量代码的效率:

    JavaScript

    var t0 = performance.now(); console.log(makeHash('Peter'));  // bad idea! var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds');

    1
    2
    3
    4
    var t0 = performance.now();
    console.log(makeHash('Peter'));  // bad idea!
    var t1 = performance.now();
    console.log('Took', (t1 - t0).toFixed(4), 'milliseconds');

    这个代码片段的在线演示如下所示:

    但是在这种情况下,我们将会测量调用makeHash(‘Peter’)函数花费的时间,以及将结果发送并打印到控制台上花费的时间。我们不知道这两个操作中每个操作具体花费多少时间, 只知道总的时间。而且,发送和打印输出的操作所花费的时间会依赖于所用的浏览器,甚至依赖于当时的上下文。

    或许你已经完美的意识到console.log方式是不可以预测的。但是执行多个函数同样是错误的,即使每个函数都不会触发I/O操作。例如:

    JavaScript

    var t0 = performance.now(); var name = 'Peter'; var result = makeHash(name.toLowerCase()).toString(); var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);

    1
    2
    3
    4
    5
    var t0 = performance.now();
    var name = 'Peter';
    var result = makeHash(name.toLowerCase()).toString();
    var t1 = performance.now();
    console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);

    同样,我们不会知道执行时间是怎么分布的。它会是赋值操作、调用toLowerCase()函数或者toString()函数吗?

    本文由金沙国际官网发布于web前端,转载请注明出处:浏览器大战能消停吗,函数的性能

    关键词: