关于settimeout:使用-setTimeout-拆解一些-CPU-密集型的执行任务

未优化之前的版本: let i = 0;let start = Date.now();function count() { // do a heavy job for (let j = 0; j < 1e9; j++) { i++; } alert("Done in " + (Date.now() - start) + 'ms');}count();上述 count 函数里的 for 循环的 i 累加,是一个 CPU 密集型工作,在执行结束之前,JavaScript 引擎工作队列里的其余工作,没有机会失去执行。 优化版本将 i 从 1 累加到 1e9 的工作,拆解成 1000 个小的子工作。每个子工作执行结束之后,调用 setTimeout 调度本身,这样工作队列里其余工作有机会失去执行。 let i = 0;let start = Date.now();function count() { // do a piece of the heavy job (*) do { i++; } while (i % 1e6 != 0); if (i == 1e9) { alert("Done in " + (Date.now() - start) + 'ms'); } else { setTimeout(count); // schedule the new call (**) }}count();第一轮工作执行:i=1...1000000第二轮工作执行:i=1000001..2000000以此类推。 ...

May 13, 2022 · 2 min · jiezi

setTimeout-或者-setInterval关于-Javascript-计时器你需要知道的一切都在这里

先来回答一下下面这个问题:对于 setTimeout(function() { console.log('timeout') }, 1000) 这一行代码,你从哪里可以找到 setTimeout 的源代码(同样的问题还会是你从哪里可以看到 setInterval 的源代码)? 很多时候,可以我们脑子里面闪过的第一个答案肯定是 V8 引擎或者其它 VM们,但是要知道的一点是,所有我们所见过的 Javascript 计时函数,都没有出现在 ECMAScript 标准中,也没有被任何 Javascript 引擎实现,计时函数,其实都是由浏览器(或者其它运行时,比如 Node.js)实现的,并且,在不同的运行时下,其表现形式有可能都不一致。 在浏览器中,主计时器函数是 Window 接口的一部分,这保证了包括如 setTimeout、setInterval 等计时器函数以及其它函数和对象能被全局访问,这才是你可以随时随地使用 setTimeout 的原因。同样的,在 Node.js 中,setTimeout 是 global 对象的一部分,这拿得你也可以像在浏览器里面一样,随时随地的使用它。 到现在可能会有一些人感觉这个问题其实并没有实际的价值,但是作为一个 Javascript 开发者,如果不知道本质,那么就有可能不能完全的理解 V8 (或者其它VM)是到底是如何与浏览器或者 Node.js 相互作用的。 暂缓一个函数的执行计时器函数都是更高阶的函数,它们可以用于暂缓一个函数的执行,或者让一个函数重复执行(由他们的第一个参数执行需要执行的函数)。 下面这是一个暂缓执行的示例: setTimeout(() => { console.log('距离函数的调用,已经过去 4 秒了')}, 4 * 1000)在上面的示例中, setTimeout 将 console.log 的执行暂缓了 4 * 1000 毫秒,也就是 4 秒钟, setTimeout 的第一个函数,就是需要暂缓执行的函数,它是一个函数的引用,下面这个示例是我们更加常见到的写法: const fn = () => { console.log('距离函数的调用,已经过去 4 秒了')}setTimeout(fn, 4 * 1000)传递参数如果被 setTimeout 暂缓的函数需要接收参数,我们可以从第三个参数开始添加需要传递给被暂缓函数的参数: ...

August 21, 2019 · 4 min · jiezi

如何在代码运行时判断定时器的状态

参考:https://stackoverflow.com/que... 问题:当前模块只需要一个定时器。但是如果有多个地方调用getData()会出现多个定时器 private timer = null; private setTimer() { this.timer = setTimeout(function () { this.getData(); }.bind(this), 5000); } getData() { http.get('getxxxData', () => { //.... this.setTimer(); }); }解决方法:在启动新的定时器之前判断上一个定时器是否正在运行,如果正在运行,就清除正在进行的定时器,再重新开启定时器。 但遗憾的是, 除了启动或停止计时器之外,没有其他方法可以与计时器交互。 在启动定时器之前检测如果定时器不为null,需要清除定时器 private timer = null; private clearPollTimer() { window.clearTimeout(this.timer); this.timer = null; } private setTimer() { if (this.timer !== null) { this.clearPollTimer(); } this.timer = setTimeout(function () { this.getData(); }.bind(this), 5000); } getData() { http.get('xxxx', () => { //.... this.setTimer(); }); }

July 10, 2019 · 1 min · jiezi

浅析-JS-中的-EventLoop-事件循环新手向

序Event Loop 这个概念相信大家或多或少都了解过,但是有一次被一个小伙伴问到它具体的原理的时候,感觉自己只知道个大概印象,于是计划着写一篇文章,用输出倒逼输入,让自己重新学习这个概念,同时也能帮助更多的人理解它~ 概念JavaScript 是一门 单线程 语言,即同一时间只能执行一个任务,即代码执行是同步并且阻塞的。 eg. 这就像只有一个窗口的银行,客户需要一个一个排队办理业务。只能同步执行肯定是有问题的,所以 JS 有了一个用来实现异步的函数:setTimeout 下面要讲的 Event Loop 就是为了确保 异步代码 可以在 同步代码 执行后继续执行的。 由于涉及到的相关概念较多,我们先从最简单的来。 队列(Queue)队列 是一种 FIFO(First In, First Out) 的数据结构,它的特点就是 先进先出 eg. 生活中最常见的例子就是排队啦,排在队伍最前面的人最先被提供服务。栈(Stack)栈 是一种 LIFO(Last In, First Out)的数据结构,特点即 后进先出。 eg. 大家都吃过桶装薯片吧~薯片在包装的时候只能从顶部放入,而吃的时候也只能从顶部拿出,这就叫后进先出哈调用栈(Call Stack)栈我们已经知道了,那么什么是 调用栈 呢 ? 它本质上当然还是个栈啦 废话,关键在于它里面装的东西,是一个个待执行的函数。 Event Loop 会一直检查 Call Stack 中是否有函数需要执行,如果有,就从栈顶依次执行。同时,如果执行的过程中发现其他函数,继续入栈然后执行。先拿两个函数来说: 栈空现在执行到一个 函数A,函数A 入栈函数A 又调用了 函数B,函数B 入栈函数B 执行完后 出栈然后继续执行 函数A,执行完后A也 出栈栈空更复杂一点的话,来看一段代码: 这段代码在 调用栈中的运行顺序如下图: 这个调用栈其实大家经常会见到,就是在控制台报错的时候,错误信息显示的就是当前时刻调用栈的状态。But, 上面我们讨论的其实都是同步代码,代码在运行的时候只用 调用栈 解释就可以了。 ...

May 28, 2019 · 1 min · jiezi

再谈谈-Promise-setTimeout-rAF-rIC

欢迎关注我的公众号睿Talk,获取我最新的文章: 一、前言Promise, setTimeout, requestAnimationFrame, requestIdleCallback 这几个概念相信很多人都很熟悉了,最近在看 React Fiber 源码的时候又对它们有了更深一层的认识,在此分享一下。下文将用 rAF 代表 requestAnimationFrame, rIC 代表 requestIdleCallback。 二、事件循环与帧事件循环和上面 4 个名词的基本概念在此不再啰嗦了,我们着重看下它们之间的关系。浏览器是一个 UI 系统,所有的操作最终都会以页面的形式展现,而页面的基本单位是帧。一帧中可能包括的任务有下面几种类型。 events: 点击事件、键盘事件、滚动事件等macro: 宏任务,如 setTimeoutmicro: 微任务,如 PromiserAF: requestAnimationFrameLayout: CSS 计算,页面布局Paint: 页面绘制rIC: requestIdleCallback理想情况下,页面会以 60 帧每秒的帧率来运行,但实际上每秒绘制多少帧是由多个因素决定的,下面举一些例子: 一个加载完成的静态页面,当用户没有进行交互的情况下,页面不需要重绘,帧率为 0。快速滚动页面的时候,可视区域的内容不断发生变化,浏览器会尽可能快的重绘页面,理想帧率为 60。假设页面有一个注册了回调的按钮,回调执行需要 500 毫秒。当点击按钮后再快速滚动页面,头 500 毫秒页面是卡住动不了的,后 500 毫秒会尽可能快的重绘页面,这时候理想帧率为 30。当使用 rAF 制作动画的时候,浏览器会尽可能快的重绘页面,桌面浏览器可能是 60 帧,移动浏览器可能是 30 帧。从上面的例子可以看出,页面的帧率不是固定的,是会动态变化的。比如某一帧中的任务占据大量时间的情况下,会影响到下一帧的执行。那么谁来调节帧率呢?显然只能依靠浏览器自身。作为开发者的我们是无法准确知道回调什么时候执行的。比如: function animation() { console.log('time: ', +new Date()); setTimeout(animate, 1000 / 60);}animation();上面的函数是假定浏览器以帧率 60 运行的,当帧率达不到的时候,2 帧之间回调可能执行了多次,也可能一次都不执行,简称掉帧。 所以在制作动画的时候,我们不能预设浏览器的帧率,正确的做法是通过 rAF 注册回调, 由浏览器来控制动画调用时机: ...

May 12, 2019 · 1 min · jiezi

js 任务

前言先看一段代码setTimeout(function() { console.log(’timeout’);}, 0);new Promise((resolve, reject) => { console.log(‘promise’); resolve();}).then(function() { console.log(’then’);});console.log(‘global’);输出: promise 、global、 then、timeoutmacrotasks 和 microtasks在 V8 实现中包含两种任务:macrotasksscript ,setTimeout, setInterval, setImmediate, I/O, UI renderingmicrotasksprocess.nextTick, Promises, Object.observe, MutationObserver执行过程如下:JavaScript 引擎首先从 macrotask queue 中取出第一个任务,执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行;浏览器进行渲染视图然后再从 macrotask queue 中取下一个,执行完毕后,再次将 microtask queue 中的全部取出;循环往复,直到两个 queue 中的任务都取完。注意:从上面可以看到,microtask 会执行完毕才进行渲染,如果 microtask 执行时间教长,会导致卡顿上面执行过程只是 chrome 的,safri 又不太一样执行过程的示例 :https://jakearchibald.com/201…如何模拟 Promise.thennew MutationObserver(function() { console.log(‘mutate’);}).observe(document.body, { attributes: true});document.body.setAttribute(‘data-random’, Math.random());setTimeout(function() { console.log(’timeout’);}, 0);new Promise((resolve, reject) => { console.log(‘promise’); resolve();}).then(function() { console.log(’then’);});console.log(‘global’);输出: promise 、global、mutate、 then、timeout参考文章:microTask: https://github.com/kaerus-com...es-promise : https://github.com/stefanpenn… ...

December 24, 2018 · 1 min · jiezi

promise与setTimeout的执行顺序问题

有一次在面试题中有做到promise与setTimeout的执行顺序,当时有点懵,执行顺序还是弄错了一点点,这里记录下1.输出setTimeout(function() { console.log(111)}, 0);setTimeout(function() { console.log(333)}, 1000);new Promise(function(resolve){ console.log(444); resolve(); console.log(555);}).then(function(){ console.log(666);});console.log(777);async function test1() { console.log(“test1”); await test2(); console.log(“test1 last”);}async function test2() { console.log(“test2”);}test1();输出结果2.个人理解首先执行同步代码,然后以事件轮询的方式执行异步代码promise中的异步体现在.then()和.catch()中而promise中的function里的是同步代码上面的代码是先执行promise里的同步代码,然后执行脚本里本身的同步代码async无论方法是同步还是异步都可以用async关键字来进行标识因为用async标识只是显示表明在该方法内,可能会用到await关键字使其变为异步方法,而且将该异步方法进行了明确的划分,只有用了await关键字时才是异步操作,其余一并为同步操作同 Generator 函数一样,async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中3.其他在网上还找到了一些资料参考了这篇文章的一些内容 参考文章setImmediate(function(){ console.log(1);},0);setTimeout(function(){ console.log(2);},0);new Promise(function(resolve){ console.log(3); resolve(); console.log(4);}).then(function(){ console.log(5);});console.log(6);process.nextTick(function(){ console.log(7);});console.log(8);输出结果: 3 4 6 8 7 5 2 1macro-task: script (整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering. micro-task: process.nextTick, Promise(原生),Object.observe,MutationObserver第一步. script整体代码被执行,执行过程为创建setImmediate macro-task创建setTimeout macro-task创建micro-task Promise.then 的回调,并执行script console.log(3); resolve(); console.log(4); 此时输出3和4,虽然resolve调用了,执行了但是整体代码还没执行完,无法进入Promise.then 流程。console.log(6)输出6process.nextTick 创建micro-taskconsole.log(8) 输出8 第一个过程过后,已经输出了3 4 6 8第二步. 由于其他micro-task 的 优先级高于macro-task。此时micro-task 中有两个任务按照优先级process.nextTick 高于 Promise,所以先输出7,再输出5第三步,micro-task 任务列表已经执行完毕,家下来执行macro-task. 由于setTimeout的优先级高于setIImmediate,所以先输出2,再输出1。优先级: promise.Trick()>promise的回调>setTimeout>setImmediate正在努力学习中,若对你的学习有帮助,留下你的印记呗(点个赞咯^_^)往期好文推荐:判断iOS和Android及PC端纯css实现瀑布流(multi-column多列及flex布局)实现多行文字及单行的省略号微信小程序之购物车和父子组件传值及calc的注意事项 ...

November 20, 2018 · 1 min · jiezi