js--ES6-Promise與經典循環閉包題

  • 首先一開始是下面這樣子的:
    for (var i = 0; i < 5; i++) {
    setTimeout(function() {
    console.log(new Date, i);
    }, 1000);
    }
    console.log(new Date, i);
    上面的代碼等同于:
    for (var i = 0; i < 5; i++) {
    function xxx() {
    console.log(new Date, i);
    }
    setTimeout(xxx, 1000);
    }
  • 注意:setTimeout里面傳入的第一個匿名函數,等價于在setTimeout語句外面定義的一個函數。所以它的閉包范圍是變量i所在的作用域,所以可以訪問到i.
  • 解析:大家都知道 i 是外面的變量,所有的setTimeout里面的函數都會指向同一個 i ,所以最終輸出:

Sat Mar 25 2017 12:40:11 GMT+0800 (中國標準時間) 5
undefined
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標準時間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標準時間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標準時間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標準時間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標準時間) 5

  • 下面改造開始:要讓控制臺能夠輸出1234
  1. 可以用閉包:(每次把新的變量i傳進一個匿名的立即執行函數,每次 j
    都能得到不同的 i ,因為j在匿名函數的作用域內,函數的執行作用域每執行一次都會重新生成,所以每次的 j 都不是同一個)
    for (var i = 0; i < 5; i++) {
    (function(j) { // j = i
    setTimeout(function() {
    console.log(new Date, j);
    }, 1000);
    })(i);
    }

            console.log(new Date, i);
    
  2. 可以用傳參法:(其實是把閉包的匿名函數擴展出來)
    function wrapper(j) {
    // function fn() {
    // console.log(new Date, j);
    // }
    // setTimeout(fn,1000);
    // 上面全部的寫法,等價于:
    setTimeout(function () {
    console.log(new Date, j);
    },1000);
    }
    for (var i = 0; i < 5; i++) {
    wrapper(i);
    }
    console.log(new Date, i);
    輸出如下:

Sat Mar 25 2017 13:20:57 GMT+0800 (中國標準時間) 5
undefined
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標準時間) 0
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標準時間) 1
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標準時間) 2
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標準時間) 3
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標準時間) 4

注意:這里有一個容易犯錯的點,setTimeout的第一個參數要求的是一個函數的引用,而不是執行一個函數,所以只能傳入函數名xxx的形式,而不能傳入xxx(a,b),這也意味著不能在xxx上直接傳參。
  • 所以,要傳參的話,只能在setTimeout外面再包裹一層函數,然后定制編寫xxx函數。
再補充一個重要知識點,如果一定要在xxx中傳參,又不想用閉包,可以使用setTimeout的第3個參數,從第3個參數往后的參數,都會傳入xxx里作為形參使用。
  • 例如:上面的代碼也可以寫成:
    setTimeout(function(j) {
    console.log(new Date, j); //可以輸出01234
    }, 1000 ,i);
  • 如果硬是在setTimeout()中傳入的xxx()的形式,那么只會以正常任務的方式立即執行xxx(),而不會放入任務隊列里去,也就是定時器失效。
  • 例如:
    for (var i=0; i<5; i++){
    function xxx(j) {
    console.log(new Date,j)
    }
    setTimeout(xxx(i),1000);
    }
    console.log(new Date, i);
    以上代碼的輸出如下:

Sat Mar 25 2017 13:37:27 GMT+0800 (中國標準時間) 0
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標準時間) 1
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標準時間) 2
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標準時間) 3
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標準時間) 4
VM2209:7 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標準時間) 5
undefined

  • 解析:可以看到,所有時間幾乎是同一時刻輸出的,而且是按012345的順序,最后退出函數返回undefined,也就是說在退出函數前,setTimeout里的xxx就已經執行了,并沒有進入任務隊列。由此可以說明,setTimeout的第一個參數期待的是函數名,而不是一個函數的執行。
    • 當然,如果xxx()執行后返回的是一個函數,理論上也可以設置定時器函數,但傳參又出現問題,太過復雜,現實暫時沒有遇到必須這樣的問題,不作討論。
ES6及Promise登場
  • 上述還可以用ES6及Promise來實現,具體如下:
    var tasks=[];
    function output(j) {
    var promise = new Promise( function(resolve, reject) {
    setTimeout(function () {
    console.log(new Date(), j);
    resolve(j);
    // console.log("這是一個小補充喲"+j);
    },1000 * i);
    });
    promise.then(function (j) {
    // console.log("這是then里的一點小補充喲"+j);
    });
    return promise;
    }
    for (var i=0;i<5;i++){
    tasks.push(output(i)); //執行順序:首先在這里將定時器設置好,
    // 也就是循環設置定時器,由于執行時間很快,每次循環的間隔可以忽略不計,
    // 所以可以認為是設置了5個時間分別為0~4s的定時器,已經開始計時。
    // 計時后返回promise對象,放在tasks數組中
    }
    // 最后,在這里相當于是一個總的監聽器,當前面4個任務都resolve以后,執行最后一個設置定時器任務,
    // 到時間以后,執行輸出5
    // 關于執行順序,前4個task是靠定時器的時間差別來決定先后輸出順序的,最后一個5的task,是依靠異步回調來執行的。
    Promise.all(tasks).then(function () {
    setTimeout(function () {
    console.log(new Date(), i);
    },1000);
    });
  • 解析:// 關于任務隊列:
    // 首先第一趟主任務隊列走下來,執行了設置定時任務,將promise對象放入tasks數組,并設置好then回調的工作。
    // 然后第二趟,執行定時任務隊列,運行consolo.log語句,
    // 然后遇到resolve,需要調用相應的then里面的回調語句(如果有的話)。
    // 但是注意,這里調用then的時機,是在本次任務的主代碼執行完畢后,
    // 也就是說,如果setTimeout語句中的resolve()后面還有執行語句,要先執行那些語句,最后才執行resolve對應的then回調。
  • 要確定resolve相應的回調語句的執行順序,可以看下面的輸出結果(將代碼里的兩句console.log去掉注釋即可):

Sat Apr 01 2017 13:15:47 GMT+0800 (中國標準時間) 0
(index):41 這是一個小補充喲0
(index):45 這是then里的一點小補充喲0
(index):39 Sat Apr 01 2017 13:15:48 GMT+0800 (中國標準時間) 1
(index):41 這是一個小補充喲1
(index):45 這是then里的一點小補充喲1
(index):39 Sat Apr 01 2017 13:15:49 GMT+0800 (中國標準時間) 2
(index):41 這是一個小補充喲2
(index):45 這是then里的一點小補充喲2
(index):39 Sat Apr 01 2017 13:15:50 GMT+0800 (中國標準時間) 3
(index):41 這是一個小補充喲3
(index):45 這是then里的一點小補充喲3
(index):39 Sat Apr 01 2017 13:15:51 GMT+0800 (中國標準時間) 4
(index):41 這是一個小補充喲4
(index):45 這是then里的一點小補充喲4
(index):60 Sat Apr 01 2017 13:15:52 GMT+0800 (中國標準時間) 5

最后強調一遍,resolve的回調函數是在本輪“事件循環”結束時執行,setTimeout(fn, 0)在下一輪“事件循環”開始時執行。
  • 如果覺得上面的例子太復雜,看下面:
    setTimeout(function () {
    console.log('three');
    }, 0);

      Promise.resolve().then(function () {
        console.log('two');
      });
     
      console.log('one');
      // one
      // two
      // three
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,804評論 2 17
  • JavaScript之父:Brendan Eich 。 -基本語法:借鑒了C語言和Java語言。-數據結構:借鑒了...
    饑人谷_kule閱讀 607評論 0 0
  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例,加載時并不主動創建,需要時才創建 最常見的單例模式,...
    Obeing閱讀 2,088評論 1 10
  • 1. 什么是閉包? 有什么作用 一個函數的執行結果為返回了另一個函數,如:function A(){ functi...
    愛上簾外修竹閱讀 242評論 0 0
  • 廢話不多說,這次直接上干貨—— -代碼走起 今天為大家分享閉包里定時器的小例子,感謝各位觀看,互相學習,一起進步。...
    努力為愛閱讀 475評論 0 1