We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"event-loop": 事件循环 "non-blocking": 非堵塞 "callback": 回调函数 "asynchronous": 异步 "single-threaded": 单线程 "concurrency": 并发 "web-api": DOM, ajax, setTimeout...
先看一张图图片出自
根据上图,首先可以得到的JS在V8引擎中有一个堆(heap)和栈(stack)的概念 堆(heap): 对象被分配的区域 栈(stack): 函数调用形成的栈帧
代码1
var a, b function foo () { return a +=1 } function bar () { return b += 2 } function baz () { bar () foo () console.log( a + b ) } baz()
解释1
栈内: 1执行baz() 进入栈 2执行bar() 进入栈 - bar() return 退出栈 3执行foo() 进入栈 - foo() return 退出栈 4执行console.log 进入栈 无return并退出栈 5baz() 执行完毕退出栈
根据图中WebApi所在的位置我们发现它并没有在V8引擎内,而是由stack内执行后再V8资源外层出现然后进入回调队列,并进行了一次event loop的事件
代码2
console.log('hi') setTimeout(function () { console.log('ha') }, 5000) console.log('heng')
解释2
栈内: 1执行console.log('hi') 进入栈 - 退出栈 2执行setTimout 进入栈 - 把回调函数cb放入浏览器资源内(相对V8) - 退出栈 3执行console.log('heng')进入栈 - 退出栈 4当前栈清空当前事件循环(event loop)结束
栈外: 5通过while(queue.length)不停的检查队列(queue)是否为空 6存放在浏览器资源内的setTimeout回调cb在5秒完成后进入队列(queue) 7事件循环while(queue.length)检查到队列(queue)有回调cb 8在当前循环内把cb推入栈内
栈内: 9执行cb,console.log('ha')进入栈 - 退出栈 10清空栈
代码3
console.log('hi') setTimeout(function () { console.log('ha') }, 0) console.log('heng')
解释3
同解释2,但是再第6步: 存放在浏览器资源内的setTimeout回调cb在5秒完成后进入队列(queue) 应该变为cb直接进入队列(queue)
代码4
console.log('hi') $.get(url, function (data) { console.log(data) }) console.log('heng')
解释4
同解释2,但是再第6步: 存放在浏览器资源内的setTimeout回调cb在5秒完成后进入队列(queue) 应该变为ajax取得数据后cb进入队列() 所以这也解释了为什么使用setTimeout来模拟ajax
代码5
console.log('start') $el.on('click', function fn() { console.log('clicked') }) setTimeout(function cb() { console.log('timeout') }, 5000) console.log('done')
解释5
栈内: 1执行console.log('start')进入栈 - 退出栈 2执行$el.on('click')进入栈 - 整个click事件包括回调函数fn放入浏览器资源内 - 退出栈 3执行setTImeout 进入栈 - 把回调函数cb放入浏览器资源内 - 退出栈 4执行console.log('done')进入栈 - 退出栈
浏览器中: 11用户点击$el触发'click' 事件,回调函数fn进入队列中 12事件循环while(queue.length)检查到队列(queue)有回调fn 13在当前循环内把fn推入栈内执行并清空
从问题5中可以知道,当我们连续不停的点击$el触发click时,队列(queue)内将会排满回调函数,这就是页面造成卡顿的原因。 造成这种情况出现最多的就是列表滚动scroll事件, 窗口resize事件。 常用的优化方法就是使用debounce去抖函数, 先看一下他的实现方法:
function debounce(fn, delay) { var timer return function() { var context = this var args = arguments clearTimeout(timer) timer = setTimeout(function() { fn.apply(context, args) }, delay) } }
分析debounce debounce函数里有一个重点,就是clearTimeout(timer) 现在模拟一个绑定事件
分析debounce
document.addEventListener('scroll', debounce( function() { console.log('scroll') }, 1000), false);
当scroll事件在栈内执行回调函数被注册到浏览器资源后,当我们触发scroll事件时,我们都会把debounce(function(){console.log('scroll')}, 1000)排到队列(queue)里,在通过事件循环放入栈内执行。
debounce(function(){console.log('scroll')}, 1000)
如果1秒内只触发1次,那么debounce函数的回调就会因为内部的setTimeout放入浏览器资源等到1秒到后排如队列内在推入栈内执行。�
但1秒内我们不停的触发scroll事件呢,那么debounce函数内部的clearTimeout(timer)将起到关键作用: 把前一次触发scroll事件放入浏览器资源的setTimeout回调给清空掉并放入新的setTimeout回调直到最后一次触发scroll,把浏览器资源内的setTimeout回调都清空只留下最后一个,等待1秒后回调排入队列(queue)等待推入栈内执行。
clearTimeout(timer)
此方法相比问题5中的情况大大减少了浏览器资源的占用,使得在固定时间内队列(queue)内都只有一个回调在等待而不是一大堆。
代码1 [1,2,3,4].forEach(function (i) { console.log(i) }) 代码2 [1,2,3,4].forEach(function (i) { setTimeout(function (i) { console.log(i) }, 0, i) })
分析 代码1中打印1,2,3,4 很明显它们都是直接在栈内执行console.log()输出的 代码2页打印相同的结果,但是不同的是每次console.log的执行都是通过setTImeout放入队列(queue)内再推入栈内执行的,这就通过浏览器资源和V8资源的区别实现了一段异步执行的代码 我们可以第二段代码改写成这样, 制作一个异步执行的forEach
分析
function asyncForEach(arr, cb) { arr.forEach(function (i) { setTimeout(cb, 0, i) }) } asyncForEach([1,2,3,4], function(i) { console.log(i) })
The text was updated successfully, but these errors were encountered:
No branches or pull requests
名词解释
JS在浏览器中的环境
先看一张图图片出自
V8引擎内的JS
根据上图,首先可以得到的JS在V8引擎中有一个堆(heap)和栈(stack)的概念
堆(heap): 对象被分配的区域
栈(stack): 函数调用形成的栈帧
问题1: 执行JS时候发生了什么
代码1
解释1
JS操作WebApi
根据图中WebApi所在的位置我们发现它并没有在V8引擎内,而是由stack内执行后再V8资源外层出现然后进入回调队列,并进行了一次event loop的事件
问题2: JS操作WebApi发生了什么?WebApi的执行不在V8内那在哪里?
代码2
解释2
问题3: 如果setTimeout(cb, 0) 会是什么情况?
代码3
解释3
问题4: ajax是什么情况?
代码4
解释4
问题5: WebApi中Event事件是什么情况?
代码5
解释5
问题6 - 列表滚动优化与Debounce去抖函数
从问题5中可以知道,当我们连续不停的点击$el触发click时,队列(queue)内将会排满回调函数,这就是页面造成卡顿的原因。
造成这种情况出现最多的就是列表滚动scroll事件, 窗口resize事件。
常用的优化方法就是使用debounce去抖函数, 先看一下他的实现方法:
分析debounce
debounce函数里有一个重点,就是clearTimeout(timer)
现在模拟一个绑定事件
此方法相比问题5中的情况大大减少了浏览器资源的占用,使得在固定时间内队列(queue)内都只有一个回调在等待而不是一大堆。
异步执行
分析
代码1中打印1,2,3,4 很明显它们都是直接在栈内执行console.log()输出的
代码2页打印相同的结果,但是不同的是每次console.log的执行都是通过setTImeout放入队列(queue)内再推入栈内执行的,这就通过浏览器资源和V8资源的区别实现了一段异步执行的代码
我们可以第二段代码改写成这样, 制作一个异步执行的forEach
The text was updated successfully, but these errors were encountered: