那曲檬骨新材料有限公司

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

js基礎(chǔ)之setTimeout與setInterval原理分析

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2024-09-19 15:10 ? 次閱讀

setTimeout與setInterval概述

setTimeout與setInterval是JavaScript引擎提供的兩個定時器方法,分別用于函數(shù)的延時執(zhí)行和循環(huán)調(diào)用。前者的主要思想是通過一個定時器,讓函數(shù)在計時結(jié)束后再執(zhí)行;后者則是每隔一定的時間,就啟動一次函數(shù)的執(zhí)行。

從原理來看,兩者似乎并不復(fù)雜。但由于JavaScript引擎是單線程的,這就讓上述兩個定時器的實際執(zhí)行變得稍微復(fù)雜了一些。下面我們來看一下兩者的運行機制與需要注意的問題。

基本原理

知識鋪墊

單線程模型:由于JavaScript被設(shè)計為用在瀏覽器環(huán)境,而該環(huán)境下存在大量可能發(fā)生沖突的DOM操作,為了避免進行復(fù)雜的沖突處理(可能存在的沖突數(shù)量幾乎不可預(yù)測),JavaScript的設(shè)計者舍棄了java的多線程模型(該模型下,執(zhí)行引擎同時可以做幾件事,但要進行線程同步),將其設(shè)計成了一門單線程語言(執(zhí)行引擎在同一時間只做一件事)。

注意:這里的單線程是指JavaScript的主線程只有一個。除了這個主線程,JavaScript還有一個I/O線程,通過事件循環(huán)來處理I/O問題,但兩者之間相對獨立,不需要進行狀態(tài)同步,因此我們?nèi)匀豢梢园袹avaScript看成一門單線程語言。

任務(wù)隊列:所謂任務(wù)隊列,就是用于存儲等待執(zhí)行的任務(wù)的隊列。由于JavaScript是一門單線程語言,如果當(dāng)前有一個任務(wù)需要執(zhí)行,但JavaScript引擎正在執(zhí)行其他任務(wù),那么這個任務(wù)就需要放進一個隊列中進行等待。等到線程空閑時,就可以從這個隊列中取出最早加入的任務(wù)進行執(zhí)行(類似于我們?nèi)ャy行排隊辦理業(yè)務(wù)。單線程相當(dāng)于說這家銀行只有一個服務(wù)窗口,一次只能為一個人服務(wù),后面到的就需要排隊,而任務(wù)隊列就是排隊區(qū),先到的就優(yōu)先服務(wù))。

注意:如果當(dāng)前線程空閑,并且隊列為空,那每次加入隊列的函數(shù)將立即執(zhí)行。

setTimeout與setInterval

setTimeout(func, delay, args) :設(shè)置超時調(diào)用。如對于setTimeout(func, 100, args),js引擎會為func函數(shù)設(shè)置一個計時器,100毫秒后,將func添加到任務(wù)隊列等待執(zhí)行。

setInterval(func, interval, args) :設(shè)置循環(huán)調(diào)用。對于語句setInterval(func, 100, args),js引擎每隔100毫秒就會把func添加到任務(wù)隊列一次。

相同點:

兩者都會加入同一個隊列,等待線程空閑時執(zhí)行。

兩者都無法保證在何時執(zhí)行回調(diào),因為無法知道線程何時空閑。

不同點

setTimeout只會將函數(shù)添加到任務(wù)隊列一次,而setInterval則是循環(huán)往隊列中添加函數(shù)。

setTimeout可以保證函數(shù)在指定的時間間隔內(nèi)不會執(zhí)行,而setInterval無法保證(有可能出現(xiàn)接近連續(xù)執(zhí)行的情況,后面會分析原因)。

運行機制

setTimeout

setTimeout的運行機制相對簡單,即在執(zhí)行該語句時,設(shè)置一個定時器,定時時間置為所設(shè)置的延時,當(dāng)計時結(jié)束后,將傳入的函數(shù)加入任務(wù)隊列,之后的執(zhí)行就交給任務(wù)隊列負責(zé)。

setTimeout函數(shù)本身會返回一個句柄,我們可以在函數(shù)執(zhí)行前通過向clearTimeout傳入該句柄取消函數(shù)的執(zhí)行。示例代碼如下:

function func(message){
	;
}
//設(shè)置100毫秒后執(zhí)行func函數(shù)
var timer = setTimeout(func, 100, "你好");

function cancel(){
	clearTimeout(timer);   //取消超時調(diào)用
}

上述代碼將在100毫秒后執(zhí)行func函數(shù),彈出一個內(nèi)容為"你好"的對話框。如果在100毫秒內(nèi)調(diào)用了cancel,就可以取消func函數(shù)的執(zhí)行。

setInterval

setInterval本質(zhì)上就是每隔一定的時間向任務(wù)隊列添加回調(diào)函數(shù)。但setInterval有一個原則:在向隊列中添加回調(diào)函數(shù)時,如果隊列中存在之前由其添加的回調(diào)函數(shù),就放棄本次添加(不會影響之后的計時)。另外也可以通過clearInterval方法移除定時器,使用方法同clearTimeout。

由于setInterval只負責(zé)定時向隊列中添加函數(shù),而不考慮函數(shù)的執(zhí)行,那么我們考慮一下下面的情況:

假設(shè)線程執(zhí)行完setInterval(func, 100, args)后處于完全空閑狀態(tài)(即只要向任務(wù)隊列添加函數(shù)就會立即執(zhí)行)。而func是一個相對復(fù)雜的函數(shù),執(zhí)行該函數(shù)需要90毫秒。那么函數(shù)的執(zhí)行過程就會變成下圖所示:

chaijie_default.png

從圖中可以看到,從上次函數(shù)執(zhí)行完畢,到下次開始執(zhí)行,之間只間隔了10毫秒,而不是我們所希望的每隔100毫秒執(zhí)行一次(因為setInterval只關(guān)注任務(wù)添加,不關(guān)注任務(wù)執(zhí)行)。

由于上述機制,在很多情況下,setInterval都會遇到一些性能問題。就拿上面的例子來說,我們的本意可能是每隔100毫秒執(zhí)行一次函數(shù),結(jié)果只等待了10毫秒就又執(zhí)行了一次。另外,對于復(fù)雜的實際情況,setInterval經(jīng)常出現(xiàn)兩次的執(zhí)行間隔相差甚遠的情況,對于用戶能感知到的操作,這會帶來很不好的用戶體驗。因此在實際編碼中,開發(fā)者通常會使用setTimeout來模擬實現(xiàn)setInterval效果(下面會有舉例)。

而如果線程一開始是繁忙的,直到150毫秒處才進入空閑狀態(tài)(假設(shè)func執(zhí)行時長為10毫秒),那么實際的運行將變成下圖所示:

chaijie_default.png

這里在100毫秒處向隊列添加func時,由于線程繁忙,上次添加的func還在隊列中等待,因此直接丟棄本次要添加的函數(shù),但在200毫秒時仍然重新向隊列中添加func。

應(yīng)用場景

setTimeout

setTimeout主要用于需要進行延時調(diào)用的場景中。如之前一篇文章介紹的js基礎(chǔ)之函數(shù)的節(jié)流與防抖,就是setTimeout典型的應(yīng)用場景。此外,由于setInterval存在的性能問題,在實際的編碼中,開發(fā)人員通常會使用setTimeout來模擬setInterval,以防止出現(xiàn)函數(shù)連續(xù)執(zhí)行的情況。如對于下面的代碼:

function func(args){
  //函數(shù)本身的邏輯
  ...
}
var timer = setInterval(func, 100, args);

我們可以通過以下代碼來實現(xiàn):

var timer;
function func(args){
  //函數(shù)本身的邏輯
  ...
  //函數(shù)執(zhí)行完后,重置定時器
  timer = setTimeout(func, 100, args);
}
timer = setTimeout(func, 100, args);

利用setTimeout保證在指定的時間內(nèi)不會執(zhí)行的特點,我們可以在執(zhí)行完上次的回調(diào)函數(shù)后,重置定時器,實現(xiàn)循環(huán)執(zhí)行func的效果,并且從上次執(zhí)行完畢到下次執(zhí)行開始,至少會經(jīng)過100毫秒。這在實際的編碼中通常會帶來較大的性能提升,同時函數(shù)的執(zhí)行間隔也會相對穩(wěn)定。

setInterval

盡管存在上述性能問題,setInterval的使用場景相對較少,但當(dāng)所使用的接口來自外部(即回調(diào)函數(shù)本身無法修改)時,就必須通過setInterval來實現(xiàn)循環(huán)執(zhí)行了。此外,對于動畫效果來說,我們通常會希望動畫運行的更加平滑(也就是希望函數(shù)運行得更頻繁),這時使用setInterval往往更加流暢,具體請參考之前的文章使用原生js實現(xiàn)簡單動畫效果。

除了這類情況,開發(fā)者一般不會使用setInterval方法進行循環(huán)調(diào)用。

補充說明

setTimeout與setInterval的第一個參數(shù)可以是一個匿名函數(shù),也可以是一個函數(shù)名,或者是一個字符串,如下面的寫法都是合法的:

function func(msg){
  ...
}
//傳入回調(diào)函數(shù)名
setTimeout(func, 100, "夕山雨");
//傳入匿名函數(shù)
setTimeout(function(name){
  ...
}, 100, "夕山雨");
//傳入字符串,js引擎會將其解析為函數(shù)體
setTimeout("", 100);

但是傳入如下的格式就可能報錯:

setTimeout(func("夕山雨"), 100);

因為這種寫法實際上是先調(diào)用func函數(shù),然后再將返回值添加到任務(wù)隊列。如果func的返回值不是函數(shù)(或可執(zhí)行的字符串),那么程序就會報錯;如果返回值是函數(shù),則會將返回的函數(shù)添加到任務(wù)隊列。該情況可以寫成下面的形式:

//將其作為字符串傳入,就可以被正確解析
setTimeout("func('夕山雨')", 100);

此外,當(dāng)給setTimeout傳入的延遲時間為0時,并不代表回調(diào)函數(shù)會立即執(zhí)行。實際上瀏覽器規(guī)定的有一個默認的最短計時時間,對于現(xiàn)代瀏覽器,這個時間一般為4毫秒(老版本的瀏覽器則會更長一些)。也就是說,即使傳入的延遲時間為0,瀏覽器也會至少在4毫秒后才會執(zhí)行。

上述補充說明同樣適用于setInterval。

總結(jié)

setTimeout與setInterval都是通過一個定時器控制回調(diào)函數(shù)的執(zhí)行,但由于javascript單線程的特點,兩者都不能準(zhǔn)確控制函數(shù)的執(zhí)行時間點,這點還請開發(fā)者注意。如果函數(shù)只需要執(zhí)行一次,很顯然我們會使用setTimeout來實現(xiàn);如果是循環(huán)執(zhí)行的情況,如果我們希望函數(shù)執(zhí)行頻率不那么高,并且間隔更穩(wěn)定,通常是使用setTimeout模擬實現(xiàn)setInterval效果。

總的來說,雖然都被用于函數(shù)延遲執(zhí)行,但兩者的運行機制有本質(zhì)上的區(qū)別,所以在使用的時候請注意區(qū)分。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 定時器
    +關(guān)注

    關(guān)注

    23

    文章

    3255

    瀏覽量

    115359
  • JS
    JS
    +關(guān)注

    關(guān)注

    0

    文章

    78

    瀏覽量

    18176
  • javascript
    +關(guān)注

    關(guān)注

    0

    文章

    525

    瀏覽量

    53942
  • 單線程
    +關(guān)注

    關(guān)注

    0

    文章

    17

    瀏覽量

    1788
收藏 人收藏

    評論

    相關(guān)推薦

    推薦一個支持js的嵌入式設(shè)備開發(fā)平臺

    可以通過vscode開發(fā)js,實時推送js代碼到設(shè)備里運行,無需編譯,支持屏幕,感興趣的可以看看 https://github.com/duoxianwulian/dxdop 提供很多js庫,可以
    發(fā)表于 09-04 14:04

    【Intel Edison試用體驗】16.node.js 入門setTimeout 的使用

    函數(shù)用delay是不可行的,所以我們會用到很多定時器,而在node.js中,使用setTimeout就可以完成定時器的任務(wù),在一定時間后再次運行某函數(shù)。var mraa = require('mraa
    發(fā)表于 07-22 10:53

    【Intel Edison試用體驗】[Demo] PWM-舵機 基于mraa設(shè)計高級函數(shù)庫 [XDK-node.js](小k - NO.13)

    傳送門:【Intel Edison試用體驗】[初見] 開箱 and 測試 (小k - NO.01) 傳送門:【Intel Edison試用體驗】[硬件] 底層硬件探索 and 啟動過程分析(小k
    發(fā)表于 08-10 17:37

    【Intel Edison試用體驗】[Demo] PWM-實現(xiàn)LED呼吸燈,原創(chuàng)二次函數(shù)方法 [XDK-node.js](小k - NO.14)

    傳送門:【Intel Edison試用體驗】[初見] 開箱 and 測試 (小k - NO.01) 傳送門:【Intel Edison試用體驗】[硬件] 底層硬件探索 and 啟動過程分析(小k
    發(fā)表于 08-10 23:21

    【Intel Edison試用體驗】[Demo] PWM-實現(xiàn)蜂鳴器Bepp奏樂 [XDK-node.js](小k - NO.15)

    傳送門:【Intel Edison試用體驗】[初見] 開箱 and 測試 (小k - NO.01) 傳送門:【Intel Edison試用體驗】[硬件] 底層硬件探索 and 啟動過程分析(小k
    發(fā)表于 08-10 23:49

    【Intel Edison試用體驗】[Demo] ADC 光線傳感器 mraa和upm [XDK-node.js](小k - NO.17)

    node.js語言,通過兩種庫“mraa” 和upm分別實現(xiàn)光學(xué)系數(shù)采集 ·工程代碼,樓主自主原創(chuàng),由于對node.js剛接觸,不熟悉,有什么優(yōu)化的地方,可以一起學(xué)習(xí)探討。 ·準(zhǔn)備工作 ·Edsion
    發(fā)表于 08-11 10:18

    買電腦與JS(奸商)較量六大要點

    買電腦與JS(奸商)較量六大要點 一般用戶在購機與JS面對面打交道的時候,一定要牢記一條真理:JS的目的就是賺錢,不會平
    發(fā)表于 01-19 17:21 ?497次閱讀

    node.jsjs要點總結(jié)

    Node.js是一個面向服務(wù)器的框架,立足于Chrome強大的V8 JS引擎。盡管它由C++編寫而成,但是它及其應(yīng)用是運行在JS上的。本文為開發(fā)者總結(jié)了4個Node.js要點。 1.
    發(fā)表于 10-13 10:39 ?0次下載

    鴻蒙系統(tǒng)中JS框架的逐行分析

    我在前文中曾經(jīng)介紹過鴻蒙的 Javascript 框架,這幾天終于把 JS 倉庫編譯通過了,期間踩了不少坑,也給鴻蒙貢獻了幾個 PR。今天我們就來逐行分析鴻蒙系統(tǒng)中的 JS 框架。 文中的所有代碼都
    的頭像 發(fā)表于 10-21 14:37 ?2046次閱讀

    使用鴻蒙JS框架寫出來的JS代碼長什么樣

    鴻蒙 JS 框架是零依賴的,只在開發(fā)打包過程中使用到了一些 npm 包。打包完的代碼是沒有依賴任何 npm 包的。
    的頭像 發(fā)表于 03-26 15:46 ?2367次閱讀

    Python怎么玩轉(zhuǎn)JS腳本

    本項目旨在讓大家了解如何用Python來執(zhí)行JS腳本,其主要目的是在進行數(shù)據(jù) 分析時,需要利用爬蟲獲取數(shù)據(jù),有時會遇到JS混淆加密反爬取難點,此時我們需 要獲取網(wǎng)頁JS加密代碼將其
    的頭像 發(fā)表于 02-23 16:26 ?1176次閱讀
    Python怎么玩轉(zhuǎn)<b class='flag-5'>JS</b>腳本

    如何破解JS加密?

    學(xué)習(xí)爬蟲最難之一無非就是如何破解JS加密,但是關(guān)于JS加密的網(wǎng)上資料非常零散雜亂,本人對這方面也略有研究,本篇文章在之前兩篇文章[Python玩轉(zhuǎn)JS腳本]
    的頭像 發(fā)表于 02-24 14:57 ?2167次閱讀
    如何破解<b class='flag-5'>JS</b>加密?

    簡述javascript定時器工作原理

    說到 javascript 中的定時器,我們肯定會想到 setTimeout() 和 setInterval() 這兩個函數(shù)。本文將從事件循環(huán)(Event Loop) 的角度來分析兩者的工作原理和區(qū)別。
    的頭像 發(fā)表于 04-21 14:32 ?848次閱讀
    簡述javascript定時器工作原理

    python爬蟲某站JS加密逆向分析

    實現(xiàn)的目標(biāo):可以通過JS加密逆向后,得到加密參數(shù),請求獲取數(shù)據(jù)。此方法同樣適用于被前端JS加密的用戶名、密碼爆破。
    的頭像 發(fā)表于 05-05 15:40 ?1829次閱讀
    python爬蟲<b class='flag-5'>之</b>某站<b class='flag-5'>JS</b>加密逆向<b class='flag-5'>分析</b>

    settimeoutsetinterval有哪些區(qū)別?

    settimeoutsetinterval有哪些區(qū)別? setTimeoutsetInterval都是JavaScript中的定時器函數(shù),用于在指定的時間間隔后執(zhí)行一段代碼。盡管它
    的頭像 發(fā)表于 12-09 14:32 ?2006次閱讀
    百家乐官网国际娱乐城| 索罗门百家乐的玩法技巧和规则 | 大发888娱乐游戏充值| 先锋百家乐官网的玩法技巧和规则 | 六合彩历史开奖记录| 24向风水| 真钱梭哈| 百家乐赢家电子书| 澳门百家乐官网小游戏| 大发888娱乐城客户端lm0| 银河百家乐官网的玩法技巧和规则| 网上现金棋牌游戏| 百家乐有电脑游戏吗| 百家乐官网的玩法视频| 全讯网博客| 24山度数| 百家乐官网网站排行| 威尼斯人娱乐城开户| 永利博百家乐官网的玩法技巧和规则 | 新东泰百家乐官网的玩法技巧和规则 | 大发888游戏平台黄埔| 网上玩百家乐犯法| 百家乐官网注册下注平台| 大发888问题缺少组件| 百家乐专打和局| 真人百家乐官网娱乐好玩| 大发888娱乐场下载 df888ylc3403 | 真人百家乐官网开户优惠| 金冠娱乐城注册| 香港百家乐玩法| 中国百家乐官网软件| 金道博彩| 百家乐德州扑克桌布| 网上百家乐官网赌博经历| 百家乐官网要怎么玩啊| 大发888游戏平台 46| 澳门百家乐海星王娱乐城| 百家乐官网十赌九诈| 8彩娱乐| 威尼斯人娱乐城存取款| 百家乐赌博怎么玩|