亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript隊(duì)列函數(shù)和異步執(zhí)行詳解

 更新時(shí)間:2017年06月19日 16:03:39   作者:hehekai  
這篇文章主要為大家詳細(xì)介紹了JavaScript隊(duì)列函數(shù)和異步執(zhí)行的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

編輯注:在Review別人的JavaScript代碼時(shí)曾看到過(guò)類似的隊(duì)列函數(shù),不太理解,原來(lái)這個(gè)是為了保證函數(shù)按順序調(diào)用。讀了這篇文章之后,發(fā)現(xiàn)還可以用在異步執(zhí)行等。

假設(shè)你有幾個(gè)函數(shù)fn1、fn2和fn3需要按順序調(diào)用,最簡(jiǎn)單的方式當(dāng)然是:

fn1();
fn2();
fn3();

但有時(shí)候這些函數(shù)是運(yùn)行時(shí)一個(gè)個(gè)添加進(jìn)來(lái)的,調(diào)用的時(shí)候并不知道都有些什么函數(shù);這個(gè)時(shí)候可以預(yù)先定義一個(gè)數(shù)組,添加函數(shù)的時(shí)候把函數(shù)push 進(jìn)去,需要的時(shí)候從數(shù)組中按順序一個(gè)個(gè)取出來(lái),依次調(diào)用:

var stack = [];
// 執(zhí)行其他操作,定義fn1
stack.push(fn1);
// 執(zhí)行其他操作,定義fn2、fn3
stack.push(fn2, fn3);
// 調(diào)用的時(shí)候
stack.forEach(function(fn) { fn() });

 這樣函數(shù)有沒(méi)名字也不重要,直接把匿名函數(shù)傳進(jìn)去也可以。來(lái)測(cè)試一下:

var stack = [];
function fn1() {
  console.log('第一個(gè)調(diào)用');
}
stack.push(fn1);

function fn2() {
  console.log('第二個(gè)調(diào)用');
}
stack.push(fn2, function() { console.log('第三個(gè)調(diào)用') });

stack.forEach(function(fn) { fn() }); // 按順序輸出'第一個(gè)調(diào)用'、'第二個(gè)調(diào)用'、'第三個(gè)調(diào)用'

這個(gè)實(shí)現(xiàn)目前為止工作正常,但我們忽略了一個(gè)情況,就是異步函數(shù)的調(diào)用。異步是JavaScript 中無(wú)法避免的一個(gè)話題,這里不打算探討JavaScript 中有關(guān)異步的各種術(shù)語(yǔ)和概念,請(qǐng)讀者自行查閱(例如某篇著名的評(píng)注)。如果你知道下面代碼會(huì)輸出1、3、2,那請(qǐng)繼續(xù)往下看:

console.log(1);

setTimeout(function() {
  console.log(2);
}, 0);

console.log(3);

假如stack 隊(duì)列中有某個(gè)函數(shù)是類似的異步函數(shù),我們的實(shí)現(xiàn)就亂套了:

var stack = [];

function fn1() { console.log('第一個(gè)調(diào)用') };
stack.push(fn1);

function fn2() {
  setTimeout(function fn2Timeout() {
     console.log('第二個(gè)調(diào)用');
  }, 0);
}
stack.push(fn2, function() { console.log('第三個(gè)調(diào)用') });

stack.forEach(function(fn) { fn() }); // 輸出'第一個(gè)調(diào)用'、'第三個(gè)調(diào)用'、'第二個(gè)調(diào)用'

 問(wèn)題很明顯,fn2確實(shí)按順序調(diào)用了,但setTimeout里的function fn2Timeout() { console.log(‘第二個(gè)調(diào)用') }卻不是立即執(zhí)行的(即使把timeout 設(shè)為0);fn2調(diào)用之后馬上返回,接著執(zhí)行fn3,fn3執(zhí)行完了然才真正輪到fn2Timeout。

怎么解決?我們分析下,這里的關(guān)鍵在于fn2Timeout,我們必須等到它真正執(zhí)行完才調(diào)用fn3,理想情況下大概像這樣:

function fn2() {
  setTimeout(function() {
    fn2Timeout();
    fn3();
  }, 0);
}

但這樣做相當(dāng)于把原來(lái)的fn2Timeout整個(gè)拿掉換成一個(gè)新函數(shù),再把原來(lái)的fn2Timeout和fn3插進(jìn)去。這種動(dòng)態(tài)改掉原函數(shù)的寫法有個(gè)專門的名詞叫Monkey Patch。按我們程序員的口頭禪:“做肯定是能做”,但寫起來(lái)有點(diǎn)擰巴,而且容易把自己繞進(jìn)去。有沒(méi)更好的做法?
我們退一步,不強(qiáng)求等f(wàn)n2Timeout完全執(zhí)行完才去執(zhí)行fn3,而是在fn2Timeout函數(shù)體的最后一行去調(diào)用:

function fn2() {
  setTimeout(function fn2Timeout() {
    console.log('第二個(gè)調(diào)用');
    fn3();    // 注{1}
  }, 0);
}

這樣看起來(lái)好了點(diǎn),不過(guò)定義fn2的時(shí)候都還沒(méi)有fn3,這fn3哪來(lái)的?

還有一個(gè)問(wèn)題,fn2里既然要調(diào)用fn3,那我們就不能通過(guò)stack.forEach去調(diào)用fn3了,否則fn3會(huì)重復(fù)調(diào)用兩次。

我們不能把fn3寫死在fn2里。相反,我們只需要在fn2Timeout末尾里找出stack中fn2的下一個(gè)函數(shù),再調(diào)用:

function fn2() {
  setTimeout(function fn2Timeout() {
    console.log('第二個(gè)調(diào)用');
    next();
  }, 0);
}

這個(gè)next函數(shù)負(fù)責(zé)找出stack 中的下一個(gè)函數(shù)并執(zhí)行。我們現(xiàn)在來(lái)實(shí)現(xiàn)next:

var index = 0;

function next() {
  var fn = stack[index];
  index = index + 1; // 其實(shí)也可以用shift 把fn 拿出來(lái)
  if (typeof fn === 'function') fn();
}

next通過(guò)stack[index]去獲取stack中的函數(shù),每調(diào)用next一次index會(huì)加1,從而達(dá)到取出下一個(gè)函數(shù)的目的。
next這樣使用:

var stack = [];

// 定義index 和next

function fn1() {
  console.log('第一個(gè)調(diào)用');
  next(); // stack 中每一個(gè)函數(shù)都必須調(diào)用`next`
};
stack.push(fn1);

function fn2() {
  setTimeout(function fn2Timeout() {
     console.log('第二個(gè)調(diào)用');
     next(); // 調(diào)用`next`
  }, 0);
}
stack.push(fn2, function() {
  console.log('第三個(gè)調(diào)用');
  next(); // 最后一個(gè)可以不調(diào)用,調(diào)用也沒(méi)用。
});

next(); // 調(diào)用next,最終按順序輸出'第一個(gè)調(diào)用'、'第二個(gè)調(diào)用'、'第三個(gè)調(diào)用'。

現(xiàn)在stack.forEach一行已經(jīng)刪掉了,我們自行調(diào)用一次next,next會(huì)找出stack中的第一個(gè)函數(shù)fn1執(zhí)行,fn1 里調(diào)用next,去找出下一個(gè)函數(shù)fn2并執(zhí)行,fn2里再調(diào)用next,依此類推。
每一個(gè)函數(shù)里都必須調(diào)用next,如果某個(gè)函數(shù)里不寫,執(zhí)行完該函數(shù)后程序就會(huì)直接結(jié)束,沒(méi)有任何機(jī)制繼續(xù)。

了解了函數(shù)隊(duì)列的這個(gè)實(shí)現(xiàn)后,你應(yīng)該可以解決下面這道面試題了:

// 實(shí)現(xiàn)一個(gè)LazyMan,可以按照以下方式調(diào)用:
LazyMan(“Hank”)
/* 輸出: 
Hi! This is Hank!
*/

LazyMan(“Hank”).sleep(10).eat(“dinner”)輸出
/* 輸出: 
Hi! This is Hank!
// 等待10秒..
Wake up after 10
Eat dinner~
*/

LazyMan(“Hank”).eat(“dinner”).eat(“supper”)
/* 輸出: 
Hi This is Hank!
Eat dinner~
Eat supper~
*/

LazyMan(“Hank”).sleepFirst(5).eat(“supper”)
/* 等待5秒,輸出
Wake up after 5
Hi This is Hank!
Eat supper
*/

// 以此類推。

Node.js 中大名鼎鼎的connect框架正是這樣實(shí)現(xiàn)中間件隊(duì)列的。有興趣可以去看看它的源碼或者這篇解讀《何為 connect 中間件》。

細(xì)心的你可能看出來(lái),這個(gè)next暫時(shí)只能放在函數(shù)的末尾,如果放在中間,原來(lái)的問(wèn)題還會(huì)出現(xiàn):

function fn() {
  console.log(1);
  next();
  console.log(2); // next()如果調(diào)用了異步函數(shù),console.log(2)就會(huì)先執(zhí)行
}

redux 和koa 通過(guò)不同的實(shí)現(xiàn),可以讓next放在函數(shù)中間,執(zhí)行完后面的函數(shù)再折回來(lái)執(zhí)行next下面的代碼,非常巧妙。有空再寫寫。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 如何基于js管理大文件上傳及斷點(diǎn)續(xù)傳詳析

    如何基于js管理大文件上傳及斷點(diǎn)續(xù)傳詳析

    文件上傳是 Web 開(kāi)發(fā)肯定會(huì)碰到的問(wèn)題,而文件夾上傳則更加難,下面這篇文章主要給大家介紹了關(guān)于如何基于js管理大文件上傳及斷點(diǎn)續(xù)傳的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • JavaScript時(shí)間格式化函數(shù)功能及使用示例

    JavaScript時(shí)間格式化函數(shù)功能及使用示例

    這篇文章主要為大家介紹了JavaScript時(shí)間格式化函數(shù)功能及使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • JavaScript事件冒泡與事件捕獲實(shí)例分析

    JavaScript事件冒泡與事件捕獲實(shí)例分析

    這篇文章主要介紹了JavaScript事件冒泡與事件捕獲,結(jié)合實(shí)例形式分析了事件冒泡、阻止冒泡以及事件捕獲的相關(guān)原理、操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2018-08-08
  • 詳解CocosCreator華容道數(shù)字拼盤

    詳解CocosCreator華容道數(shù)字拼盤

    這篇文章主要介紹了詳解CocosCreator華容道數(shù)字拼盤,對(duì)華容道感興趣的同學(xué),看完之后,可以回去親手試一下
    2021-04-04
  • D3.js 實(shí)現(xiàn)帶伸縮時(shí)間軸拓?fù)鋱D的示例代碼

    D3.js 實(shí)現(xiàn)帶伸縮時(shí)間軸拓?fù)鋱D的示例代碼

    這篇文章主要介紹了D3.js 實(shí)現(xiàn)帶伸縮時(shí)間軸拓?fù)鋱D的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • TypeScript命名空間合并講解

    TypeScript命名空間合并講解

    這篇文章主要介紹了TS命名空間合并講解,回顧上一節(jié)的內(nèi)容,在上一節(jié)中我們介紹了TS中最常見(jiàn)的聲明合并:接口合并,今天要講的內(nèi)容也是TS中的聲明合并,但這次是命名空間相關(guān)的合并,需要的朋友可以參考一下
    2021-12-12
  • 微信小程序上傳圖片實(shí)例

    微信小程序上傳圖片實(shí)例

    這篇文章主要為大家詳細(xì)介紹了微信小程序上傳圖片實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • JS實(shí)現(xiàn)自動(dòng)變換的菜單效果代碼

    JS實(shí)現(xiàn)自動(dòng)變換的菜單效果代碼

    這篇文章主要介紹了JS實(shí)現(xiàn)自動(dòng)變換的菜單效果代碼,可實(shí)現(xiàn)自動(dòng)變換菜單選中項(xiàng)的技巧,涉及JavaScript定時(shí)函數(shù)觸發(fā)頁(yè)面樣式屬性變換的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-09-09
  • 微信小程序?qū)崿F(xiàn)書架小功能

    微信小程序?qū)崿F(xiàn)書架小功能

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)書架小功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • JavaScript使用indexOf獲得子字符串在字符串中位置的方法

    JavaScript使用indexOf獲得子字符串在字符串中位置的方法

    這篇文章主要介紹了JavaScript使用indexOf獲得子字符串在字符串中位置的方法,涉及javascript中indexOf方法操作字符串的技巧,需要的朋友可以參考下
    2015-04-04

最新評(píng)論