-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdetail_of_throttle.html
1 lines (1 loc) · 27.6 KB
/
detail_of_throttle.html
1
<!doctype html><html lang="zh-CN" class="night"><head><meta charset="utf-8"><meta content="width=device-width,initial-scale=1,maximum-scale=4,user-scalable=0" name="viewport"><title>Ede's Blog</title><meta name="description" content="Try to be a qualified programmer"><meta property="og:type" content="website"><meta property="og:description" content="Try to be a qualified programmer"><meta property="og:title" content="Ede's Blog"><meta property="og:site_name" content="Ede's Blog"><meta property="og:url" content="https://ede.ink"><meta property="og:image" content="https://edeity.oss-cn-shenzhen.aliyuncs.com/public/edeity_o.png"><link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"><link rel="mainfest" href="/mainfest.json"><link rel="stylesheet" href="/public/css/common.css"><link rel="stylesheet" href="//at.alicdn.com/t/font_707055_4b9og9sc5lx.css"><script>!function(){var e=-1!==window.location.search.indexOf("theme=night")||"night"===window.localStorage.getItem("edeity-theme_theme"),t=-1!==window.location.search.indexOf("theme=light")||"light"===window.localStorage.getItem("edeity-theme_theme");(new Date).getHours();var n=document.querySelector("html");e?n.classList.add("night"):t?n.classList.remove("night"):n.classList.add("night")}(),document.addEventListener("DOMContentLoaded",function(){null!==document.querySelector("ol.toc")&&(document.querySelector("#nav-bar").style.cssText="display: block")})</script><script async src="https://www.googletagmanager.com/gtag/js?id=G-M3J9QSEE2Z"></script><script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-M3J9QSEE2Z")</script><meta name="generator" content="Hexo 5.0.0"></head><body><div class="loading"></div><div id="switch" data-switch="{"toc":true,"use_pwa":false}"></div><header class="fullscreen"><div class="toolbar"><i class="iconfont icon-menu"></i></div><h1><a href="/">Ede's Blog</a></h1><div class="head-link"><a class="btn waves" href="/"><span><i class="iconfont icon-home">Home </i></span></a><a class="btn waves" href="/about/index.html"><span><i class="iconfont icon-me">About </i></span></a><a class="btn waves" target="_blank" rel="noopener" href="https://github.com/edeink"><span><i class="iconfont icon-github">Github</i></span></a></div></header><div class="some-link"><a class="btn" id="light-or-not"><i class="iconfont icon-light"></i> </a><a style="display:none" class="btn" id="up-to-top"><i class="iconfont icon-up"></i></a></div><div id="nav-bar" style="display:none"><div class="toc"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Round-1%EF%BC%9Athrottle%E6%98%AF%E7%AB%8B%E5%8D%B3%E8%BF%94%E5%9B%9E"><span class="toc-number">1.</span> <span class="toc-text">Round 1:throttle是立即返回</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Round-2%EF%BC%9A-trailing%E7%94%A8%E6%B3%95"><span class="toc-number">2.</span> <span class="toc-text">Round 2: trailing用法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Round-3%EF%BC%9A-throttle%E4%B8%8D%E8%83%BD%E7%BB%91%E5%AE%9A%E5%A4%9A%E4%B8%AA%E5%90%8C%E6%BA%90%E5%87%BD%E6%95%B0"><span class="toc-number">3.</span> <span class="toc-text">Round 3: throttle不能绑定多个同源函数</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Round4-Lodash%E4%BB%A5%E5%8F%8AUnderscore%E7%9A%84%E7%BB%86%E5%B0%8F%E5%B7%AE%E5%BC%82"><span class="toc-number">4.</span> <span class="toc-text">Round4: Lodash以及Underscore的细小差异</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%93%E8%AE%BA"><span class="toc-number">5.</span> <span class="toc-text">结论</span></a></li></ol></div></div><main id="content-main" class="section"><div class="list-item"><h1 class="post-title"><a id="throttle细节小记" class="article-link" href="">throttle细节小记</a></h1><div class="post-meta"><time class="meta published">Mar 11, 2020</time></div><div class="article"><div class="post-excerpt markdown-body"><p>说起来也惭愧,我一个工作三年有余的人,竟然hold不住只有二三十行的<code>throttle</code>函数。自从工程用了我写的throttle后,遇到的几个坑或bug,自挂东南,自以警示世人。</p><p>先说定义,<code>throttle</code>,译<strong>节流</strong>,基本用法 :</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> throttleFunc = throttle(func, <span class="number">60</span>);</span><br></pre></td></tr></table></figure><p>无论调用多少次<code>throttleFunc</code>,<code>func</code>在规定的时间(60ms)内只执行一次。常见的用法有,前端页面动效等变更,一般情况下,保证60FPS即可,即<code>let throttleRender = throttle(render, 1000 / 60)</code>。</p><p>废话不多讲,看坑。</p><h2 id="Round-1:throttle是立即返回"><a href="#Round-1:throttle是立即返回" class="headerlink" title="Round 1:throttle是立即返回"></a>Round 1:throttle是立即返回</h2><p>此标题有两层含义</p><ol><li>假如函数有返回值,throttle修饰后的函数也应有返回值</li><li>throttle修饰后的函数,假如满足了调用间隔,应<strong>立即</strong>执行此函数</li></ol><p>为啥?因为throttle自带缓存功能!<del>有些人</del>我有时会忘记利用<code>throttle</code>有缓存的能力,而直接在函数内赋值给全局变量,再读取全局变量,这样函数就不是纯函数,不优雅。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> foo = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">longTimeToCalc</span>(<span class="params"></span>) </span>{</span><br><span class="line"> ++foo;</span><br><span class="line"> <span class="keyword">return</span> foo;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> throttleCalc = throttle(longTimeToCalc, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> a = throttleCalc(); <span class="comment">// a = 1</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> b = throttleCalc(); <span class="comment">// b = 1; 即第一次执行的缓存结果</span></span><br><span class="line">}, <span class="number">50</span>);</span><br></pre></td></tr></table></figure><p>至于立即执行,则容易理解,最基础,我们总是希望得到较新值嘛。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> foo = <span class="number">0</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">longTimeToCalc</span>(<span class="params"></span>) </span>{</span><br><span class="line"> ++foo;</span><br><span class="line"> <span class="keyword">return</span> foo;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> throttleCalc = throttle(longTimeToCalc, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> a = throttleCalc(); <span class="comment">// a = 1</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// b = 2; 满足100ms间隔后,应立即执行并返回较新值</span></span><br><span class="line"> <span class="keyword">let</span> b = throttleCalc();</span><br><span class="line">}, <span class="number">110</span>);</span><br></pre></td></tr></table></figure><h2 id="Round-2:-trailing用法"><a href="#Round-2:-trailing用法" class="headerlink" title="Round 2: trailing用法"></a>Round 2: trailing用法</h2><p><del>某些人</del>我不知道,throttle可以设置运行细节,其中一个参数是trailing,默认值为<code>true</code>,其作用是:最小时间间隔Xms内假如发生多次调用,是否在最后一次主动调用后,再执行一次该方法。看例子:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> func = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ ++i }</span><br><span class="line"><span class="keyword">let</span> throttleFunc = throttle(func, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">throttleFunc();</span><br><span class="line">throttleFunc();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在第一次调用后,未满足100ms间隔再次调用时,会在第一次调用后,再调用一次</span></span><br><span class="line"><span class="built_in">console</span>.log(i); <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(i); <span class="comment">// 1</span></span><br><span class="line">}, <span class="number">50</span>);</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(i); <span class="comment">// 2</span></span><br><span class="line">}, <span class="number">110</span>);</span><br></pre></td></tr></table></figure><p>值得注意,假如没有发生<strong>多次调用</strong>,不会“多”执行的。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> func = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ ++i }</span><br><span class="line"><span class="keyword">let</span> throttleFunc = throttle(func, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">throttleFunc();</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(i); <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(i); <span class="comment">// 1,因为没有多次调用</span></span><br><span class="line">}, <span class="number">110</span>);</span><br></pre></td></tr></table></figure><p>假如值为false,结果如下:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> func = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{ ++i }</span><br><span class="line"><span class="keyword">let</span> throttleFunc = throttle(func, <span class="number">100</span>, {<span class="attr">trailing</span>: <span class="literal">false</span>});</span><br><span class="line"></span><br><span class="line">throttleFunc();</span><br><span class="line">throttleFunc();</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(i); <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(i); <span class="comment">// 1</span></span><br><span class="line">}, <span class="number">50</span>);</span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(i); <span class="comment">// 1,最后一次调用被取消了</span></span><br><span class="line">}, <span class="number">110</span>);</span><br></pre></td></tr></table></figure><p><del>非常简单,我明白了。</del></p><p>为什么<code>traling</code>要默认为true,因为最后一次调用得到的值,往往是我们最想要的(参考Round 1)。但某些场景下,traling不能为true。比如鼠标区选文字时<small>(按照常规逻辑,应在鼠标按下时进入选区模式,拖拽时选择字符,鼠标松开时结束)</small>:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onStart</span>(<span class="params"></span>) </span>{ <span class="built_in">console</span>.log(<span class="string">'start'</span>) }</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onMove</span>(<span class="params"></span>) </span>{ <span class="built_in">console</span>.log(<span class="string">'move'</span>) }</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onEnd</span>(<span class="params"></span>) </span>{ <span class="built_in">console</span>.log(<span class="string">'end'</span>) }</span><br><span class="line"></span><br><span class="line"><span class="comment">// move触发非常频繁,加个限流</span></span><br><span class="line"><span class="keyword">const</span> onThrottleMove = throttle(onMove, <span class="number">1000</span> / <span class="number">60</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.addEventlistener(<span class="string">'mousedown'</span>, onStart);</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'mousemove'</span>, onThrottleMove);</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'mouseup'</span>, onEnd);</span><br></pre></td></tr></table></figure><p>看起来木有什么问题,但是,因为<code>throttle</code>触发频繁时往往不是立即调用,会有一定的延后性。所以你可能会看到这样的输出:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">start</span><br><span class="line">move</span><br><span class="line">move</span><br><span class="line">move</span><br><span class="line">end <span class="comment">// 松开鼠标,且不再移动</span></span><br><span class="line">move <span class="comment">// 多了一个move</span></span><br></pre></td></tr></table></figure><p>移动选词时鼠标都松开了,还会多执行一次move选词,不符合常理。因此,那些需要保证<strong>严格执行顺序</strong>的方法,最好<code>trailing</code>设置为false。即:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ...</span></span><br><span class="line"><span class="keyword">const</span> onThrottleMove = throttle(onMove, <span class="number">1000</span> / <span class="number">60</span>, { <span class="attr">trailing</span>: <span class="literal">false</span> });</span><br></pre></td></tr></table></figure><h2 id="Round-3:-throttle不能绑定多个同源函数"><a href="#Round-3:-throttle不能绑定多个同源函数" class="headerlink" title="Round 3: throttle不能绑定多个同源函数"></a>Round 3: throttle不能绑定多个同源函数</h2><p>假如使用throttle不优雅,也会产生bug,参考代码:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 统一的事件入口</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">onEvent</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">switch</span>(event.type) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'mousemove'</span>: {</span><br><span class="line"> <span class="comment">// 这里触发一个自定义的‘MOVE’事件</span></span><br><span class="line"> <span class="keyword">const</span> selEvent = docuemnt.createEvent(<span class="string">'MOVE'</span>);</span><br><span class="line"> selEvent.initCustomEvent(type);</span><br><span class="line"> <span class="built_in">window</span>.dispatchEvent(selEvent);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'MOVE'</span>: {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'MOVE'</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> onThrottleEvent = throttle(onEvent, <span class="number">30</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'mousemove'</span>, onThrottleEvent);</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'MOVE'</span>, onThrottleEvent);</span><br></pre></td></tr></table></figure><p>初步理解,频繁移动鼠标时,只要保证调用时间间隔满足30ms,便会不断输出<code>MOVE</code>。</p><p>然而并没有!!<code>MOVE</code>触发的次数少得可怜,甚至几秒也不会触发一次。</p><p>为什么?因为throttle绑定的是onEvent,以上写法只保证<strong>onEvent触发时间间隔满足30ms</strong>。当频繁移动鼠标时,事件可能进入了<code>case 'mousemove'</code>这个逻辑分支,从而霸占了<code>case 'MOVE'</code>的生存空间了。所以说,同源函数最好绑定多个函数。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// ...onEvent</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> onThrottleEvent = throttle(onEvent, <span class="number">30</span>);</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'mousemove'</span>, onThrottleEvent);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不要那么小气,多绑定一次又不会怀孕</span></span><br><span class="line"><span class="keyword">const</span> onThrottleMouseMove = throttle(onEvent, <span class="number">30</span>);</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'MOVE'</span>, onThrottleMouseMove);</span><br></pre></td></tr></table></figure><h2 id="Round4-Lodash以及Underscore的细小差异"><a href="#Round4-Lodash以及Underscore的细小差异" class="headerlink" title="Round4: Lodash以及Underscore的细小差异"></a>Round4: Lodash以及Underscore的细小差异</h2><p>某次发现,lodash的单元测试用例,underscore是有一定概率不满足的:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 以下是lodash的测试用例</span></span><br><span class="line">it(<span class="string">'subsequent calls should return the result of the first call'</span>, <span class="function">(<span class="params">done</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> identity = <span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> value;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">let</span> throttled = throttle(identity, <span class="number">32</span>);</span><br><span class="line"> <span class="keyword">let</span> results = [throttled(<span class="string">'a'</span>), throttled(<span class="string">'b'</span>)];</span><br><span class="line"> </span><br><span class="line"> expect(results).to.eql([<span class="string">'a'</span>, <span class="string">'a'</span>]);</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> results = [throttled(<span class="string">'c'</span>), throttled(<span class="string">'d'</span>)];</span><br><span class="line"> <span class="comment">// underscore: 有一定概率是["b", "b"]</span></span><br><span class="line"> expect(results).to.eql([<span class="string">'c'</span>, <span class="string">'c'</span>]); </span><br><span class="line"> done();</span><br><span class="line"> }, <span class="number">64</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>这是不是说underscore不稳定?并不是,经过一番折腾,我发现以下测试用例,underscore符合,lodash则不满足的:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">it(<span class="string">'[自定义]: 即使阻塞,两次调用间隔也大于最小调用间隔'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">let</span> callCount = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> timeout = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">let</span> throttled = throttle(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> callCount++;</span><br><span class="line"> }, <span class="number">100</span>);</span><br><span class="line"> throttled();</span><br><span class="line"> throttled();</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> throttled();</span><br><span class="line"> <span class="comment">// lodash此时会是3,underscore是2</span></span><br><span class="line"> expect(callCount).to.be.equal(<span class="number">2</span>);</span><br><span class="line"> }, <span class="number">220</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 通过循环300毫秒来阻塞js(浏览器将推迟调用↑setTimeout)</span></span><br><span class="line"> <span class="keyword">let</span> execTime = <span class="number">300</span>;</span><br><span class="line"> <span class="keyword">let</span> startTime = <span class="built_in">Date</span>.now();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; timeout !== <span class="literal">true</span>; i++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Date</span>.now() - startTime > execTime) {</span><br><span class="line"> timeout = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>结论:<code>Lodash</code>以<strong>调用时</strong>作为基准,调用即重置计时器;而<code>underscore</code>会以<strong>调用完成</strong>作为基准,方法调用成功后才重置。</p><p>不懂?但我<del>也不是很懂</del>懒得说了,大伙看源码去吧。这个差异会导致一个问题。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">longTimeToExec</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// 假设这里的代码耗时200毫秒</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> lodashThrottleExec = lodash.throttle(longTimeToExec, <span class="number">100</span>);</span><br><span class="line"><span class="keyword">let</span> underscoreThrottleExec = _.throttle(longTimeToExec, <span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">lodashThrottleExec();</span><br><span class="line">underscoreThrottleExec();</span><br><span class="line"></span><br><span class="line"><span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> lodashThrottleExec();</span><br><span class="line"> underscoreThrottleExec();</span><br><span class="line">}, <span class="number">110</span>);</span><br></pre></td></tr></table></figure><p>在这种场景下,假如使用<code>lodashThrottleExec</code>,会有问题。110ms时,即使第一次执行<strong>未完成</strong>,但间隔已满足大于100ms,再次调用<code>longTimeToExec</code>会触发执行。当然,所有的throttle都采用了<code>requestAnimate</code>,并不会出现卡死现象,但可能浏览器将疲于执行<code>LongTimeToExec</code>,使其它方法响应不够及时。</p><p>这种差异其实是一种取舍,lodash更保守,调用更频繁,得到的数据也更准确,所以我<del>选择underscore</del>选择在完成时重置计时器,毕竟性能使我头大。</p><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>也许还有更多的坑没有发现。但经此一役,那些看起来简单的方法,也可能蕴含众多细节。程序猿嘛,就要对代码保持敬畏之心。</p></div></div></div><div class="more section"><div class="pre"><a class="article-link" href="/mock_scroll_bar.html"><i class="iconfont icon-right"></i> <span>滑动优化填坑记</span></a></div><div class="next"><a class="article-link" href="/search_of_auto_test.html">前端自动化测试探索 <i class="iconfont icon-right"></i></a></div></div></main></body><footer class="section fullscreen"><div class="footer-desc">Edeink © 2015-2022 · Powered by Hexo</div></footer><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script><script src="/public/js/init.js"></script></html>