JS定時器 setTimeout 異步

異步 參考文章

  • 函數(shù)調用和函數(shù)的區(qū)別
// 函數(shù)和函數(shù)調用的區(qū)別

function fn () { //函數(shù)
    return 1;
}

fn(); //函數(shù)調用
console.log(fn); // ? fn() {return 1;}
console.log(fn()); //1

// 函數(shù)返回函數(shù)的時候調用當前函數(shù)即為return的那個函數(shù)
function fn2 () {
    return function () {
        return 2;
    }
}
fn2//是一個函數(shù)
fn2()//也是一個函數(shù) function () { return 2; }
fn2()(); //2

定時器

  • 先看一個例子
function fn (){
    console.log(1);
}
setTimeout(fn);
console.log(2);
//上述代碼輸出 2 1撼港,先輸出2后輸出1

setTimeout設定了一個輸出1的事件在0秒后執(zhí)行俭茧,那么瀏覽器會先將目前的代碼都執(zhí)行完成后再回頭執(zhí)行setTimeout中的代碼

function fn (){
    console.log(1);
}
var timeId = setTimeout(fn);
console.log('id:' + timeId);//1
console.log(2);
timeId = setTimeout(fn);
console.log('id:' + timeId);//2

//每次要執(zhí)行setTimeout的函數(shù)的時候,就要銷毀這個setTimeout催什,然后下次在定義個相同的setTimeout的時候潭袱,id是不一樣的栋烤。
  • 間隔永遠要比要比設置的久
function fn (){
    console.log(1);
    console.log(new Date()-0);
}
setTimeout(fn);
console.log(new Date()-0);
// 設置了時間延時為0虎敦,但是輸出的date還是有幾十毫秒的差別,這是因為瀏覽器就算是延時為0沥潭,也要先將當前頁面執(zhí)行完邀泉,等‘閑下來’的時候再來執(zhí)行setTimeout
// 所以這樣造成的結果是,間隔永遠要比要比設置的久
  • 定時器的‘怠惰’問題
    當瀏覽器失去焦點的時候钝鸽,即用戶不在關注瀏覽器的時候汇恤,定時器的輸出會變慢,比如設置0.5秒執(zhí)行一次拔恰,有可能最小化瀏覽器之后1秒才執(zhí)行一次這么做是為了節(jié)省內存因谎。

  • 關于setInterval
    setInteval只有一個timerId,和setTimeout不同

  • setTimeout的函數(shù)的傳參

    setTimeout(fn,500,1,2,3);
    function fn () {
        console.log(arguments); //[1,2,3]
    }
  • 怎么取消定時器 window.clearTimeout(timerId)
    var timerId = setTimeout('console.log("設置了clearTimeout,這條會被阻止執(zhí)行")',3000);
    window.clearTimeout(timerId); //阻止前面的setTimeout執(zhí)行
    var timerId2 = setTimeout('console.log("沒設置clearTimeout,這條會被執(zhí)行")',3000);

異步 和 回調

看下面兩個例子,異步就是不等結果颜懊,去執(zhí)行別的代碼财岔,讓一個回調函數(shù)來通知我結果

    function queue () {
        setTimeout(function () {
            return "您的號碼是233";
        },60000); //60s之后才能拿到結果
    };

    var result = queue();
    console.log(result); //undefined 因為queue這個函數(shù)沒有寫return,所以默認返回undefined  拿不到想要的匿名函數(shù)的返回值

這時候想要拿到匿名函數(shù)的返回值,可以創(chuàng)建一個對象或者函數(shù),傳入queue中饭冬,queue中的匿名函數(shù)不用設置return使鹅,因為設置了也拿不到 然后不設置return,直接讓這個創(chuàng)建的代理函數(shù)獲取到值昌抠,就可以輸出.

    function queue (agent) {  //傳入agent給形參
        setTimeout(function () {
            agent("您的號碼是233"); // 匿名函數(shù)里不寫return,寫了也拿不到鲁僚,直接用agent函數(shù)獲取這個值
        },3000); //60s之后才能拿到結果
    };

//    這時候想要拿到匿名函數(shù)的返回值,可以創(chuàng)建一個對象或者函數(shù),傳入queue中炊苫,queue中的匿名函數(shù)不用設置return,因為設置了也拿不到
//    然后不設置return冰沙,直接讓這個創(chuàng)建的代理函數(shù)獲取到值侨艾,就可以輸出
    function agent (result) { //創(chuàng)建一個函數(shù)
        console.log(result);
    }
    queue(agent); //將創(chuàng)建的函數(shù)傳入queue

整體思路是,程序還要接著執(zhí)行下面的拓挥,創(chuàng)建一個代理函數(shù)去得到值唠梨,等setTimeout的時間間隔到了之后拿到值之后返回給程序。

在上述例子中侥啤,agent函數(shù)就是一個回調函數(shù)当叭。由全局傳遞給queue茬故,然后queue調用之后又傳回給全局。叫做回調函數(shù)蚁鳖。 “我傳給你磺芭,你調用再傳回給我”

異步和回調往往是同時出現(xiàn)的。因為異步了之后不回調醉箕,就相當于不要這個返回結果了钾腺。

相關的一些問題分析

for (var i = 0; i < 5; i ++) {
    console.log(i); //0 1 2 3 4
}

for (var i = 0; i < 5; i ++) {
    setTimeout(function () {  //for循環(huán)線結束,才運行setTimeout  這時候i的值都是5
        console.log(i);
    },1000*i);
}

第一次馬上輸出5 第二次是1000毫秒之后輸出5 第三次是2000毫秒之后輸出5 以此類推到第五次,j即每一秒打印一個
setTimeout內部匿名函數(shù)要輸出i時讥裤,找不到i放棒,要到上一級也就是全局中找i,這時候i已經(jīng)為5了己英,而外部的時間間隔i跟全局的i是同一個東西哨查,每次的值為0,1,2,3,4

for (var i = 0; i < 5; i ++) {
    (function () {
        setTimeout(function () {
            console.log(i);
        },i * 1000);
    })(i);
}

和第二題的結果一樣,1000乘i的那個i是是全局中的i剧辐,每次為0,1,2,3,4

而setTimeout里面的函數(shù)的作用域中要輸出的i不存在寒亥,要到上一級中作用域即立即執(zhí)行函數(shù)中找i,立即執(zhí)行函數(shù)中也沒有i荧关,再到全局作用域中找i溉奕,這時候for循環(huán)已經(jīng)結束了,i為5忍啤,所以每次輸出5,

for (var i = 0; i < 5; i ++) {
    (function (i) {
        setTimeout(function () {
            console.log(i);
        },i * 1000);
    })(i);
}

和第3題不同加勤,這里立即執(zhí)行函數(shù)寫了一個形參,這個形參相當于一個變量聲明var i = arguments[0];

那么setTimeout里面的函數(shù)在輸出i時同波,找不到i鳄梅,到上一級作用域即立即執(zhí)行函數(shù)中去找,和第3題不同的是這次立即執(zhí)行函數(shù)里面有i了未檩,不會再到全局作用域里找i了戴尸。

寫了形參就相當于定義了i,所以這時候輸出的i是立即執(zhí)行函數(shù)中的i冤狡,這個i傳入的值就是當時的全局中i的值0,1,2,3,4

for (var i = 0; i < 5; i ++) {
    setTimeout((function (i) {
        console.log(i);
    })(i),i * 1000)
}

第5題仍然通過作用域鏈進行理解孙蒙,setTimeout里面的函數(shù)的作用域中要輸出的i,就是立即執(zhí)行函數(shù)中的i悲雳,因為定義了形參挎峦,這個形參每次接收的i是當時全局中的i的值為0,1,2,3,4. 而時間間隔的i就是全局中的i,每次的值為0,1,2,3,4合瓢。
求得結果之后坦胶,針對第5題,我們可以簡化一下代碼,仔細分析一下

    for (var i = 0; i < 5; i ++) {
        var t1 = function (i) {
            console.log(i);
        };
        var t2 = t1(i);
        setTimeout(t2,1000*i);
    }

那么把代碼拆開之后我們再來分析顿苇,此時setTimeout里面執(zhí)行的是t2峭咒,t2是t1調用的結果,t1里面沒有寫return岖圈,那么默認返回undefined讹语,所以t2為undefined,此時其實就相當于:

    setTimeout(undefined,0);
    setTimeout(undefined,1000);
    setTimeout(undefined,2000);
    setTimeout(undefined,3000);
    setTimeout(undefined,4000);

而輸出的值是t1中的console.log(i);我們也可以很清晰的看到蜂科,這個i就是傳入的當時的每次的i值0,1,2,3,4

setTimeout的應用:做個倒計時器

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>


<select placeholder="選擇一個時間" name="" id="mySelect">
    <option value="1" selected>1分鐘</option>
    <option value="5">5分鐘</option>
    <option value="10">10分鐘</option>
    <option value="20">20分鐘</option>
</select>
<button id="start">開始計時</button>
<button id="pause" disabled>暫停計時</button>
<button id="resume" disabled>恢復計時</button>

<div id="show"></div>


<script>

    var show = document.querySelector('#show');
    var start = document.querySelector('#start');
    var pause = document.querySelector('#pause');
    var resume = document.querySelector('#resume');
    var select = document.querySelector('#mySelect');


//    var timeLeft;
//    function showtime () {
//        show.innerText = timeLeft + '秒';
//        timeLeft -= 1;
//        if (timeLeft === 0) {
//            return;
//        }
//        setTimeout(showtime,1000);
//    }
//    start.addEventListener('click',function () {
//        var seconds = 60 * parseInt(select.value);
//        timeLeft = seconds;
//        showtime();
//    });
// 上面的代碼能實現(xiàn)效果但是存在bug顽决,連續(xù)點擊start倒計時會越來越快,有多個setTimeout在控制計時
// 所以要解決這個問題导匣,在再一次點擊start的時候才菠,要將上一次的setTimeout清除掉,怎么得到上一次的setTimeout呢,可以通過設置timerId

    var timeLeft;
    var lastTimerId; // 全局中定義個lastTinmerId
    function showtime () {
        show.innerText = timeLeft + '秒';
        timeLeft -= 1;
        if (timeLeft === 0) {
            return;
        }
        lastTimerId = setTimeout(showtime,1000); //執(zhí)行seTimeout時順便給timerId賦值
    }
    start.addEventListener('click',function () {
        var seconds = 60 * parseInt(select.value);
        timeLeft = seconds;
//        執(zhí)行函數(shù)前先清除上一次的setTimeout
        if (lastTimerId !== undefined) {
            window.clearTimeout(lastTimerId);
        }
        showtime();
        pause.disabled = false; //點了start才能點擊pause
    });

//    接下來設置暫停和恢復
//    暫停的思路很簡單,恢復的思路就是不要重新賦值timeLeft,保持暫停的時候的timeLeft就可以
    pause.addEventListener('click',function () {
        if (lastTimerId) {
            window.clearTimeout(lastTimerId);
        }
        resume.disabled = false; //點了pause才能點resume
        pause.disabled = true; //點了pause之后不能再點擊pause
    })
    resume.addEventListener('click',function () {
        showtime(); //就一行代碼贡定, showTime()就是把當前的timeLeft打出來赋访,并沒有給timeLeft重新賦值
        pause.disabled = false; //點了resume之后就可以點pause
        resume.disabled = true; //點了resume之后,不可以繼續(xù)點擊resume
    })
</script>
</body>
</html>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末缓待,一起剝皮案震驚了整個濱河市蚓耽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旋炒,老刑警劉巖步悠,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瘫镇,居然都是意外死亡鼎兽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門铣除,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谚咬,“玉大人,你說我怎么就攤上這事尚粘≡褙裕” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵背苦,是天一觀的道長互捌。 經(jīng)常有香客問我,道長行剂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任钳降,我火速辦了婚禮厚宰,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己铲觉,他們只是感情好澈蝙,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撵幽,像睡著了一般灯荧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盐杂,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天逗载,我揣著相機與錄音,去河邊找鬼链烈。 笑死厉斟,一個胖子當著我的面吹牛,可吹牛的內容都是我干的强衡。 我是一名探鬼主播擦秽,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漩勤!你這毒婦竟也來了感挥?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤越败,失蹤者是張志新(化名)和其女友劉穎触幼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眉尸,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡域蜗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了噪猾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霉祸。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖袱蜡,靈堂內的尸體忽然破棺而出丝蹭,到底是詐尸還是另有隱情,我是刑警寧澤坪蚁,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布奔穿,位于F島的核電站,受9級特大地震影響敏晤,放射性物質發(fā)生泄漏贱田。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一嘴脾、第九天 我趴在偏房一處隱蔽的房頂上張望男摧。 院中可真熱鬧蔬墩,春花似錦、人聲如沸耗拓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乔询。三九已至樟插,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間竿刁,已是汗流浹背黄锤。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留们妥,地道東北人猜扮。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像监婶,于是被迫代替她去往敵國和親旅赢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內容

  • 弄懂js異步 講異步之前惑惶,我們必須掌握一個基礎知識-event-loop煮盼。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,707評論 0 5
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法带污,內部類的語法僵控,繼承相關的語法,異常的語法鱼冀,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • http://python.jobbole.com/85231/ 關于專業(yè)技能寫完項目接著寫寫一名3年工作經(jīng)驗的J...
    燕京博士閱讀 7,557評論 1 118
  • 在線閱讀 http://interview.poetries.top[http://interview.poetr...
    程序員poetry閱讀 114,315評論 24 450
  • 第一部分 準入訓練 第1章 進入忍者世界 js開發(fā)人員通常使用js庫來實現(xiàn)通用和可重用的功能报破。這些庫需要簡單易用,...
    如201608閱讀 1,345評論 1 2