JavaScript異步問(wèn)題解決方案

1. 前言

JavaScript是一門(mén)單線(xiàn)程語(yǔ)言,在執(zhí)行一些比較耗時(shí)的操作(比如常見(jiàn)的Ajax請(qǐng)求)時(shí)湘换,為了不阻塞后面代碼的執(zhí)行蚀苛,往往需要執(zhí)行異步操作。關(guān)于JS的運(yùn)行機(jī)制糊识,大家可以看阮一峰的這篇文章:JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop

如何處理異步操作在一直是個(gè)值得關(guān)注的問(wèn)題绩社,我會(huì)在這篇文章里介紹幾種常見(jiàn)的處理異步函數(shù)的解決方案

2. 使用回調(diào)函數(shù)

如果你有使用過(guò)JQuery,那么肯定會(huì)熟悉這樣的處理方式赂苗,回調(diào)函數(shù)是一個(gè)作為變量傳遞給另外一個(gè)函數(shù)的函數(shù)愉耙,它在主體函數(shù)執(zhí)行完之后執(zhí)行。

let delayWithCallback = (time, callback) => {
    console.log('handle...')
    setTimeout(() => {
        if (typeof callback === 'function') {
            callback(`success`)
        }
    }, time)
}

在callback方法里處理回調(diào)

3. 使用Promise

Promise構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù)拌滋,該函數(shù)的兩個(gè)參數(shù)分別是resolve和reject朴沿,分別表示異步操作執(zhí)行成功后的回調(diào)函數(shù)和異步操作執(zhí)行失敗后的回調(diào)函數(shù)。這里說(shuō)的“成功”和“失敗”主要是為了便于理解败砂,準(zhǔn)確的說(shuō)法是resolve和reject改變了Promise的狀態(tài)赌渣,resolve將Promise的狀態(tài)置為resolved,reject將Promise的狀態(tài)置為rejected

let index = 1;
let delayWithPromise = (time) => {
    return new Promise((resolve, reject) => {
        console.log(`task${index} handle...`)
        index++
        setTimeout(() => {
            resolve('success')
        }, time)
    })
}

3.1 在Promise實(shí)例上使用then方法里處理回調(diào)

then方法是Promise原型上的方法昌犹,Promise.prototype.then()坚芜,then方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)是Resolved狀態(tài)的回調(diào)函數(shù)祭隔,第二個(gè)參數(shù)(可選)是Rejected狀態(tài)的回調(diào)函數(shù)

let func2 = () => {
    console.log('start')
    delayWithPromise(1000).then(result => {
        console.log(result)
        console.log('end')
    })
} 
func2()

3.2 多個(gè)異步操作

假定有下面的異步方法货岭,用于獲取學(xué)生信息

let getJSON = (key) => {
    const data = {
        stu: 'stu1',
        stu1: {
            age: 1
        },
        stu2: {
            age: 2
        }
    }
    return new Promise((resolve, reject) => {
        setTimeout(function () {
            resolve(data[key])
        }, 100);
    })
}

Promise.all方法用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例疾渴。此時(shí)多個(gè)Promise實(shí)例可以同步執(zhí)行千贯,返回值是一個(gè)數(shù)組,包含每個(gè)操作的結(jié)果

let func3 = () => {
    Promise.all([
        getJSON('stu1'),
        getJSON('stu2')
    ]).then(stu => {
        console.log(stu)
    })
}
func3()

3.3 鏈?zhǔn)秸{(diào)用解決Promise異步嵌套問(wèn)題

在上面的代碼中搞坝,我們不關(guān)心操作執(zhí)行的順序搔谴,Promise.all方法可以很好的解決多個(gè)異步操作同時(shí)執(zhí)行的問(wèn)題; 如果當(dāng)前的異步操作依賴(lài)上一個(gè)操作的結(jié)果桩撮,就很容易寫(xiě)出func4()這樣的代碼敦第,在func4()中,嵌套的層級(jí)還比較少(2層)店量,如果有20芜果、30層呢,這樣代碼就難以維護(hù)了

let func4 = () => {
    getJSON('stu').then(result1 => {
        getJSON(result1).then(result2 => {
            .....
        })
    })
}
func4()

如果我們?cè)趖hen方法中返回的是一個(gè)新的Promise實(shí)例融师,就可以形成鏈?zhǔn)秸{(diào)用關(guān)系了

采用then鏈?zhǔn)秸{(diào)用右钾,它避免了異步函數(shù)之間的層層嵌套,將原來(lái)異步函數(shù)的“嵌套關(guān)系”轉(zhuǎn)變?yōu)楸阌陂喿x和理解的“鏈?zhǔn)健辈襟E關(guān)系,可以指定一組按照次序調(diào)用的回調(diào)函數(shù)舀射,就像func5()窘茁,此時(shí)代碼的結(jié)構(gòu)會(huì)清晰很多

let func5 = () => {
    getJSON('stu')
        .then(result1 => {
            return getJSON(result1)
        }).then(result2 => {
            console.log(result2)
        })
}
func5()

4. 采用async/await解決異步問(wèn)題

async/await是ES7中的新特性,下面是關(guān)于async的幾個(gè)要點(diǎn):

  • 在function前面加async關(guān)鍵字表示這是一個(gè)async函數(shù)
  • async的返回值是一個(gè)Promise對(duì)象脆烟,你可以用then方法添加回調(diào)函數(shù)
  • await后面跟著的應(yīng)該是一個(gè)promise對(duì)象山林,如果不是,會(huì)被轉(zhuǎn)成一個(gè)立即resolve的Promise對(duì)象
  • await表示在這里等待promise返回結(jié)果了邢羔,再繼續(xù)執(zhí)行驼抹。
let func6 = async () => {
    console.log('start')
    let result = await delayWithPromise(1000);
    console.log(result)
    console.log('end')
}
func6()

4.1 async/await處理多個(gè)異步問(wèn)題

一個(gè)一個(gè)的執(zhí)行

let func7 = async () => {
    console.log('start')
    let result1 = await delayWithPromise(500)
    let result2 = await delayWithPromise(500)
    console.dir(result1, result2)
    console.log('end')
}
func7()

同時(shí)執(zhí)行

let func8 = async () => {
    console.log('start')
    let [result1, result2] = await Promise.all([
        delayWithPromise(500),
        delayWithPromise(500)
    ])
    console.dir(result1, result2)
}
func8()

func7()和func8()的在處理時(shí)會(huì)有些不同,在func7()里會(huì)先打印task1 handle...拜鹤,500ms之后砂蔽,再打印task2 handle...

但是在func8()中,task1 handle...task2 handle...是同時(shí)打印的署惯,這說(shuō)明在func7()任務(wù)是一個(gè)一個(gè)順序阻塞執(zhí)行的,在func8()是同時(shí)同步執(zhí)行的

總結(jié)一下镣隶,在含有多個(gè)異步操作的方法中,如果你的代碼邏輯里面存在相互依賴(lài)關(guān)系,比如當(dāng)前操作依賴(lài)上一個(gè)操作的結(jié)果升筏,那么你可以使用func7()這樣的寫(xiě)法

如果你的異步操作之間沒(méi)有依賴(lài)關(guān)系外构,你就應(yīng)該使用func8()這樣的寫(xiě)法,這樣前面的await不會(huì)阻塞后面的異操作域那,所有操作同時(shí)咙边,可以大大提高效率

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市次员,隨后出現(xiàn)的幾起案子败许,更是在濱河造成了極大的恐慌,老刑警劉巖淑蔚,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件市殷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡刹衫,警方通過(guò)查閱死者的電腦和手機(jī)醋寝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)带迟,“玉大人音羞,你說(shuō)我怎么就攤上這事〔秩” “怎么了嗅绰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我办陷,道長(zhǎng)貌夕,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任民镜,我火速辦了婚禮啡专,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘制圈。我一直安慰自己们童,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布鲸鹦。 她就那樣靜靜地躺著慧库,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馋嗜。 梳的紋絲不亂的頭發(fā)上齐板,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音葛菇,去河邊找鬼甘磨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛眯停,可吹牛的內(nèi)容都是我干的济舆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼莺债,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼滋觉!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起齐邦,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤椎侠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后侄旬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體肺蔚,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年儡羔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宣羊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡汰蜘,死狀恐怖仇冯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情族操,我是刑警寧澤苛坚,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布比被,位于F島的核電站,受9級(jí)特大地震影響泼舱,放射性物質(zhì)發(fā)生泄漏等缀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一娇昙、第九天 我趴在偏房一處隱蔽的房頂上張望尺迂。 院中可真熱鬧,春花似錦冒掌、人聲如沸噪裕。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)膳音。三九已至,卻和暖如春铃诬,著一層夾襖步出監(jiān)牢的瞬間祭陷,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工趣席, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颗胡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓吩坝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親哑蔫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钉寝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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

  • 異步編程對(duì)JavaScript語(yǔ)言太重要。Javascript語(yǔ)言的執(zhí)行環(huán)境是“單線(xiàn)程”的闸迷,如果沒(méi)有異步編程嵌纲,根本...
    呼呼哥閱讀 7,309評(píng)論 5 22
  • 弄懂js異步 講異步之前,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop腥沽。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,710評(píng)論 0 5
  • Promise 對(duì)象 Promise 的含義 Promise 是異步編程的一種解決方案逮走,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,705評(píng)論 1 56
  • async 函數(shù) 含義 ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),使得異步操作變得更加方便今阳。 async 函數(shù)是...
    huilegezai閱讀 1,259評(píng)論 0 6
  • 歡迎閱讀專(zhuān)門(mén)探索 JavaScript 及其構(gòu)建組件的系列文章的第四章师溅。 在識(shí)別和描述核心元素的過(guò)程中,我們還分享...
    OSC開(kāi)源社區(qū)閱讀 1,152評(píng)論 1 10