js--ES6-Promise與經(jīng)典循環(huán)閉包題

  • 首先一開始是下面這樣子的:
    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里面?zhèn)魅氲牡谝粋€(gè)匿名函數(shù)戏仓,等價(jià)于在setTimeout語句外面定義的一個(gè)函數(shù)。所以它的閉包范圍是變量i所在的作用域户辱,所以可以訪問到i.
  • 解析:大家都知道 i 是外面的變量济瓢,所有的setTimeout里面的函數(shù)都會(huì)指向同一個(gè) i ,所以最終輸出:

Sat Mar 25 2017 12:40:11 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5
undefined
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5
VM87:3 Sat Mar 25 2017 12:40:12 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5

  • 下面改造開始:要讓控制臺(tái)能夠輸出1234
  1. 可以用閉包:(每次把新的變量i傳進(jìn)一個(gè)匿名的立即執(zhí)行函數(shù)崎淳,每次 j
    都能得到不同的 i 蝇裤,因?yàn)閖在匿名函數(shù)的作用域內(nèi)掷漱,函數(shù)的執(zhí)行作用域每執(zhí)行一次都會(huì)重新生成全封,所以每次的 j 都不是同一個(gè))
    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. 可以用傳參法:(其實(shí)是把閉包的匿名函數(shù)擴(kuò)展出來)
    function wrapper(j) {
    // function fn() {
    // console.log(new Date, j);
    // }
    // setTimeout(fn,1000);
    // 上面全部的寫法马昙,等價(jià)于:
    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 (中國標(biāo)準(zhǔn)時(shí)間) 5
undefined
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 0
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 1
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 2
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 3
VM94:8 Sat Mar 25 2017 13:20:58 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 4

注意:這里有一個(gè)容易犯錯(cuò)的點(diǎn),setTimeout的第一個(gè)參數(shù)要求的是一個(gè)函數(shù)的引用刹悴,而不是執(zhí)行一個(gè)函數(shù)行楞,所以只能傳入函數(shù)名xxx的形式,而不能傳入xxx(a,b)土匀,這也意味著不能在xxx上直接傳參子房。
  • 所以,要傳參的話就轧,只能在setTimeout外面再包裹一層函數(shù)证杭,然后定制編寫xxx函數(shù)。
再補(bǔ)充一個(gè)重要知識(shí)點(diǎn)妒御,如果一定要在xxx中傳參解愤,又不想用閉包,可以使用setTimeout的第3個(gè)參數(shù)乎莉,從第3個(gè)參數(shù)往后的參數(shù)送讲,都會(huì)傳入xxx里作為形參使用。
  • 例如:上面的代碼也可以寫成:
    setTimeout(function(j) {
    console.log(new Date, j); //可以輸出01234
    }, 1000 ,i);
  • 如果硬是在setTimeout()中傳入的xxx()的形式惋啃,那么只會(huì)以正常任務(wù)的方式立即執(zhí)行xxx()哼鬓,而不會(huì)放入任務(wù)隊(duì)列里去,也就是定時(shí)器失效肥橙。
  • 例如:
    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 (中國標(biāo)準(zhǔn)時(shí)間) 0
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 1
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 2
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 3
VM2209:3 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 4
VM2209:7 Sat Mar 25 2017 13:37:27 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5
undefined

  • 解析:可以看到魄宏,所有時(shí)間幾乎是同一時(shí)刻輸出的,而且是按012345的順序存筏,最后退出函數(shù)返回undefined宠互,也就是說在退出函數(shù)前,setTimeout里的xxx就已經(jīng)執(zhí)行了椭坚,并沒有進(jìn)入任務(wù)隊(duì)列予跌。由此可以說明,setTimeout的第一個(gè)參數(shù)期待的是函數(shù)名善茎,而不是一個(gè)函數(shù)的執(zhí)行券册。
    • 當(dāng)然,如果xxx()執(zhí)行后返回的是一個(gè)函數(shù),理論上也可以設(shè)置定時(shí)器函數(shù)烁焙,但傳參又出現(xiàn)問題航邢,太過復(fù)雜,現(xiàn)實(shí)暫時(shí)沒有遇到必須這樣的問題骄蝇,不作討論膳殷。
ES6及Promise登場
  • 上述還可以用ES6及Promise來實(shí)現(xiàn),具體如下:
    var tasks=[];
    function output(j) {
    var promise = new Promise( function(resolve, reject) {
    setTimeout(function () {
    console.log(new Date(), j);
    resolve(j);
    // console.log("這是一個(gè)小補(bǔ)充喲"+j);
    },1000 * i);
    });
    promise.then(function (j) {
    // console.log("這是then里的一點(diǎn)小補(bǔ)充喲"+j);
    });
    return promise;
    }
    for (var i=0;i<5;i++){
    tasks.push(output(i)); //執(zhí)行順序:首先在這里將定時(shí)器設(shè)置好九火,
    // 也就是循環(huán)設(shè)置定時(shí)器赚窃,由于執(zhí)行時(shí)間很快,每次循環(huán)的間隔可以忽略不計(jì)岔激,
    // 所以可以認(rèn)為是設(shè)置了5個(gè)時(shí)間分別為0~4s的定時(shí)器勒极,已經(jīng)開始計(jì)時(shí)。
    // 計(jì)時(shí)后返回promise對象虑鼎,放在tasks數(shù)組中
    }
    // 最后辱匿,在這里相當(dāng)于是一個(gè)總的監(jiān)聽器,當(dāng)前面4個(gè)任務(wù)都resolve以后震叙,執(zhí)行最后一個(gè)設(shè)置定時(shí)器任務(wù)掀鹅,
    // 到時(shí)間以后,執(zhí)行輸出5
    // 關(guān)于執(zhí)行順序媒楼,前4個(gè)task是靠定時(shí)器的時(shí)間差別來決定先后輸出順序的乐尊,最后一個(gè)5的task,是依靠異步回調(diào)來執(zhí)行的划址。
    Promise.all(tasks).then(function () {
    setTimeout(function () {
    console.log(new Date(), i);
    },1000);
    });
  • 解析:// 關(guān)于任務(wù)隊(duì)列:
    // 首先第一趟主任務(wù)隊(duì)列走下來扔嵌,執(zhí)行了設(shè)置定時(shí)任務(wù),將promise對象放入tasks數(shù)組夺颤,并設(shè)置好then回調(diào)的工作痢缎。
    // 然后第二趟,執(zhí)行定時(shí)任務(wù)隊(duì)列世澜,運(yùn)行consolo.log語句独旷,
    // 然后遇到resolve,需要調(diào)用相應(yīng)的then里面的回調(diào)語句(如果有的話)寥裂。
    // 但是注意嵌洼,這里調(diào)用then的時(shí)機(jī),是在本次任務(wù)的主代碼執(zhí)行完畢后封恰,
    // 也就是說麻养,如果setTimeout語句中的resolve()后面還有執(zhí)行語句,要先執(zhí)行那些語句诺舔,最后才執(zhí)行resolve對應(yīng)的then回調(diào)鳖昌。
  • 要確定resolve相應(yīng)的回調(diào)語句的執(zhí)行順序备畦,可以看下面的輸出結(jié)果(將代碼里的兩句console.log去掉注釋即可):

Sat Apr 01 2017 13:15:47 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 0
(index):41 這是一個(gè)小補(bǔ)充喲0
(index):45 這是then里的一點(diǎn)小補(bǔ)充喲0
(index):39 Sat Apr 01 2017 13:15:48 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 1
(index):41 這是一個(gè)小補(bǔ)充喲1
(index):45 這是then里的一點(diǎn)小補(bǔ)充喲1
(index):39 Sat Apr 01 2017 13:15:49 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 2
(index):41 這是一個(gè)小補(bǔ)充喲2
(index):45 這是then里的一點(diǎn)小補(bǔ)充喲2
(index):39 Sat Apr 01 2017 13:15:50 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 3
(index):41 這是一個(gè)小補(bǔ)充喲3
(index):45 這是then里的一點(diǎn)小補(bǔ)充喲3
(index):39 Sat Apr 01 2017 13:15:51 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 4
(index):41 這是一個(gè)小補(bǔ)充喲4
(index):45 這是then里的一點(diǎn)小補(bǔ)充喲4
(index):60 Sat Apr 01 2017 13:15:52 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間) 5

最后強(qiáng)調(diào)一遍,resolve的回調(diào)函數(shù)是在本輪“事件循環(huán)”結(jié)束時(shí)執(zhí)行许昨,setTimeout(fn, 0)在下一輪“事件循環(huán)”開始時(shí)執(zhí)行懂盐。
  • 如果覺得上面的例子太復(fù)雜,看下面:
    setTimeout(function () {
    console.log('three');
    }, 0);

      Promise.resolve().then(function () {
        console.log('two');
      });
     
      console.log('one');
      // one
      // two
      // three
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末糕档,一起剝皮案震驚了整個(gè)濱河市允粤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌翼岁,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件司光,死亡現(xiàn)場離奇詭異琅坡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)残家,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進(jìn)店門榆俺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坞淮,你說我怎么就攤上這事茴晋。” “怎么了回窘?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵诺擅,是天一觀的道長。 經(jīng)常有香客問我啡直,道長烁涌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任酒觅,我火速辦了婚禮撮执,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘舷丹。我一直安慰自己抒钱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布颜凯。 她就那樣靜靜地躺著谋币,像睡著了一般。 火紅的嫁衣襯著肌膚如雪装获。 梳的紋絲不亂的頭發(fā)上瑞信,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天,我揣著相機(jī)與錄音穴豫,去河邊找鬼凡简。 笑死逼友,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的秤涩。 我是一名探鬼主播帜乞,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼筐眷!你這毒婦竟也來了黎烈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤匀谣,失蹤者是張志新(化名)和其女友劉穎照棋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體武翎,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡烈炭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宝恶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片符隙。...
    茶點(diǎn)故事閱讀 40,742評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垫毙,靈堂內(nèi)的尸體忽然破棺而出霹疫,到底是詐尸還是另有隱情,我是刑警寧澤综芥,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布丽蝎,位于F島的核電站,受9級特大地震影響膀藐,放射性物質(zhì)發(fā)生泄漏征峦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一消请、第九天 我趴在偏房一處隱蔽的房頂上張望栏笆。 院中可真熱鬧,春花似錦臊泰、人聲如沸蛉加。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽针饥。三九已至,卻和暖如春需频,著一層夾襖步出監(jiān)牢的瞬間丁眼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工昭殉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苞七,地道東北人藐守。 一個(gè)月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像蹂风,于是被迫代替她去往敵國和親卢厂。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品惠啄,去做同樣的事情慎恒,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,777評論 2 17
  • JavaScript之父:Brendan Eich 撵渡。 -基本語法:借鑒了C語言和Java語言融柬。-數(shù)據(jù)結(jié)構(gòu):借鑒了...
    饑人谷_kule閱讀 598評論 0 0
  • 單例模式 適用場景:可能會(huì)在場景中使用到對象,但只有一個(gè)實(shí)例趋距,加載時(shí)并不主動(dòng)創(chuàng)建丹鸿,需要時(shí)才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,076評論 1 10
  • 1. 什么是閉包? 有什么作用 一個(gè)函數(shù)的執(zhí)行結(jié)果為返回了另一個(gè)函數(shù)棚品,如:function A(){ functi...
    愛上簾外修竹閱讀 239評論 0 0
  • 廢話不多說,這次直接上干貨—— -代碼走起 今天為大家分享閉包里定時(shí)器的小例子廊敌,感謝各位觀看铜跑,互相學(xué)習(xí),一起進(jìn)步骡澈。...
    努力為愛閱讀 464評論 0 1