欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

詳解JavaScript異步編程之a(chǎn)sync和await

admin
2024年4月1日 15:5 本文熱度 903

經(jīng)過了Generator的過渡之后異步代碼同步化的需求逐漸成為了主流需求,雖然Generator函數(shù)能夠?qū)崿F(xiàn)異步編程,但實(shí)際上我們很少用它來實(shí)現(xiàn)異步,因?yàn)?/span>在ES7版本中得到了提案,并在ES8版本中進(jìn)⾏了實(shí)現(xiàn)的 async 函數(shù)對Generator函數(shù)的流程又做了一層封裝,定義了全新的異步控制流程,使得異步方案使用更加方便。


01
介紹


async function foo(){
 await ...
 await ...
}
foo()

async/await的代碼結(jié)構(gòu)的編寫⽅式與Generator函數(shù)結(jié)構(gòu)很相似,async就相當(dāng)于那個(gè)(*),await就相當(dāng)于yield。提案中規(guī)定了可以使⽤async修飾⼀個(gè)函數(shù),這樣就能在該函數(shù)的直接⼦作⽤域中,使⽤await來⾃動(dòng)的控制函數(shù)的流程,await 右側(cè)可以編寫任何變量或?qū)ο螅?dāng)右側(cè)是普通對象的時(shí)候函數(shù)會(huì)⾃動(dòng)返回右側(cè)的結(jié)果并向下執(zhí)⾏,⽽當(dāng)await右側(cè)為Promise對象時(shí),如果Promise對象狀態(tài)沒有變成完成,函數(shù)就會(huì)掛起等待,直到Promise對象變成fulfilled,程序再向下執(zhí)⾏,并且Promise的值會(huì)⾃動(dòng)返回給await左側(cè)的變量中。async和await需要成對出現(xiàn),async可以單獨(dú)修飾函數(shù),但是await只能在被async修飾的函數(shù)中使⽤。


有了await和async就相當(dāng)于使⽤了⾃帶執(zhí)⾏函數(shù)的Generator函數(shù),這樣我們就不再需要單獨(dú)針對Generator函數(shù)進(jìn)⾏開發(fā)了,所以async和await逐漸成為主流異步流程控制的終極解決⽅案。⽽Generator慢慢淡出了業(yè)務(wù)開發(fā)者的舞臺(tái),不過Generator函數(shù)成為了向下兼容過渡期版本瀏覽器的候補(bǔ)實(shí)現(xiàn)⽅式,雖然在現(xiàn)今的⼤部分項(xiàng)⽬業(yè)務(wù)中使⽤Generator函數(shù)的場景⾮常的少,但是如果查看腳⼿架項(xiàng)⽬中通過babel構(gòu)建的JavaScript⽣產(chǎn)代碼,我們還是能⼤量的發(fā)現(xiàn)Generator的應(yīng)⽤的,它的作⽤就是為了兼容不⽀持async和await的瀏覽器。


02
介紹async


async的英文意思是異步,當(dāng)函數(shù)前面有async關(guān)鍵字并且該函數(shù)有返回值時(shí),函數(shù)執(zhí)行成功,函數(shù)就會(huì)調(diào)用Promise.resove()并隱式的返回一個(gè)Promise對象;如果函數(shù)執(zhí)行失敗就會(huì)調(diào)用Promise.reject()并返回一個(gè)Promise對象。

async function foo(){
  return 1
}
let res = foo()
console.log(res)

根據(jù)控制臺(tái)結(jié)果我們發(fā)現(xiàn)其實(shí)async修飾的函數(shù),本身就是⼀個(gè)Promise對象,雖然我們在函數(shù)中return的值是1,是使⽤了async修飾之后,這個(gè)函數(shù)運(yùn)⾏時(shí)并沒有直接返回1,⽽是返回了⼀個(gè)值為1的Promise對象。


async函數(shù)中如果有異步操作會(huì)進(jìn)行等待,但是async函數(shù)本身會(huì)馬上返回,不會(huì)阻塞當(dāng)前線程。async函數(shù)被調(diào)用不會(huì)阻塞界面渲染,內(nèi)部由await關(guān)鍵字修飾異步過程,會(huì)阻塞等待異步任務(wù)的完成再返回。

如果在函數(shù)中return一個(gè)直接量,async會(huì)把這個(gè)直接量通過Promise.resolve(直接量) 封裝成 Promise 對象 ,如果沒有返回值,相當(dāng)于返回了Promise.resolve(undefined)。

我們可以通過Promise.then()回調(diào)得到async函數(shù)的返回值,因?yàn)樵摵瘮?shù)返回的是Promise對象。

async function foo(){
  return 1
}
let res = foo()
console.log(res)

foo().then((res) => {
  console.log('res的值是', res)
})

只有async函數(shù)內(nèi)部的異步操作執(zhí)行完,才會(huì)執(zhí)行then方法指定的回調(diào)函數(shù)。


看下面代碼:

async function foo(){
    console.log(3)
    return 1;
}
console.log(1)
foo()
console.log(2)

通過控制臺(tái)看到打印順序?yàn)?、3、2。按照Promise對象的執(zhí)⾏流程function被async修飾之后它本身應(yīng)該變成異步函數(shù),那么它應(yīng)該在1和2輸出完畢之后再輸出3,但是結(jié)果卻出⼈意料,這是為什么呢?

我們回想一下Promise函數(shù)的結(jié)構(gòu)。

new Promise(function(){
    
}).then(function(){

})

在介紹Promise對象時(shí),我們知道new Promise時(shí)的function是同步流程,而then()是異步的,這也就不難解釋為什么輸出結(jié)果是1、3、2了。


03
介紹await


await的英文意思是等待,等待的是一個(gè)表達(dá)式,這個(gè)表達(dá)式的計(jì)算結(jié)果是 Promise 對象或者其它值,得到resolve的值作為await表達(dá)式的運(yùn)算結(jié)果。

因?yàn)?async 函數(shù)返回一個(gè) Promise 對象,所以 await 可以用于等待一個(gè) async 函數(shù)的返回值,這也可以說是 await 在等 async 函數(shù),實(shí)際上它等待的是一個(gè)返回值。await 不僅僅用于等 Promise 對象,它可以等任意表達(dá)式的結(jié)果。


await 表達(dá)式的運(yùn)算結(jié)果取決于它等的東西。如果它等到的不是一個(gè) Promise 對象,相當(dāng)于 await Promise.resolve(...),那 await 表達(dá)式的運(yùn)算結(jié)果就是它等到的東西。

如果它等到的是一個(gè) Promise 對象,它會(huì)阻塞后面的代碼,等著 Promise 對象 resolve,然后得到 resolve 的值,作為 await 表達(dá)式的運(yùn)算結(jié)果。

依然以上面的代碼為例,稍加改造。

async function foo(){
  console.log(3);
  var a = await 4;
  console.log(a);
  return 1;
}

console.log(1)
foo()
console.log(2)

通過控制臺(tái)我們可以看到打印順序?yàn)?->3->2->4。可以看到await 4表達(dá)式會(huì)將4作為其運(yùn)算結(jié)果賦值給a,并且await會(huì)阻塞后面的console.log(a)的執(zhí)行,所以最后才會(huì)打印出4,這就是 await 必須用在 async 函數(shù)中的原因。

我們將上面的函數(shù)翻譯⼀下,由于async修飾的函數(shù)會(huì)被解釋成Promise對象,所以我們可以將其翻譯成如下結(jié)構(gòu):

console.log(1)
new Promise(function(resolve){
    console.log(3)
    resolve(4)
}).then(function(a){
    console.log(a)
})
console.log(2)

看到這個(gè)Promise對象我們就明白了,由于初始化的回調(diào)是同步的所以1,3,2都是同步代碼,⽽4是在resolve中傳⼊的,then代表異步回調(diào)所以4應(yīng)該最后輸出。

綜上所述,async函數(shù)中有⼀個(gè)最大的特點(diǎn),就是第⼀個(gè)await會(huì)作為分⽔嶺⼀般的存在,在第⼀個(gè)await的右側(cè)和上⾯的代碼,全部是同步代碼區(qū)域相當(dāng)于new Promise的回調(diào),第⼀個(gè)await的左側(cè)和下⾯的代碼,就變成了異步代碼區(qū)域相當(dāng)于then的回調(diào)。


04
await的優(yōu)勢


假設(shè)一個(gè)業(yè)務(wù),分多個(gè)步驟完成,每個(gè)步驟都是異步的,而且依賴于上一個(gè)步驟的結(jié)果。在過去的編程中JavaScript的主要異步處理⽅式,是采⽤回調(diào)函數(shù)的⽅式來進(jìn)⾏處理,想要保證n個(gè)步驟的異步編程有序進(jìn)⾏,會(huì)出現(xiàn)如下的代碼。

setTimeout(function(){
  //第⼀秒后執(zhí)⾏的邏輯
  console.log('第⼀秒之后發(fā)⽣的事情')
  setTimeout(function(){
    //第⼆秒后執(zhí)⾏的邏輯
    console.log('第⼆秒之后發(fā)⽣的事情')
      setTimeout(function(){
      //第三秒后執(zhí)⾏的邏輯
      console.log('第三秒之后發(fā)⽣的事情')
    },1000)
  },1000)
},1000)

雖然可以Promise 通過 then 鏈來解決多層回調(diào)的問題,但是現(xiàn)在有了async/await我們可使用它來進(jìn)一步優(yōu)化上面的代碼。

// 假設(shè)起始值為1,每個(gè)步驟都是異步的,而且依賴于上一個(gè)步驟的計(jì)算結(jié)果,且每間隔一秒打印結(jié)果
function runTask(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 1), 1000);
    });
}

function task1(n) {
    console.log(`step1 with ${n}`);
    return runTask(n);
}

function task2(n) {
    console.log(`step2 with ${n}`);
    return runTask(n);
}

function task3(n) {
    console.log(`step3 with ${n}`);
    return runTask(n);
}

用 Promise 方式來實(shí)現(xiàn)這三個(gè)步驟。

function run() {
    const sum1 = 1;
    task1(sum1)
        .then(sum2 => task2(time2))
        .then(time3 => task3(time3))
        .then(result => {
            console.log(result) // 4
        });
}
run();

如果用 async/await 來實(shí)現(xiàn)如下。

async function run() {
    const sum1 = 1;
    const sum2 = await task1(sum1);
    const sum3 = await task2(sum2);
    const result = await task3(sum3);
    console.log(`result is ${result}`); // result is 4
}

run();

現(xiàn)在我們可以使⽤如上的⽅式來進(jìn)⾏流程控制,不再需要依賴自己定義的流程控制器函數(shù)來進(jìn)⾏分步執(zhí)⾏,這⼀切的核⼼起源都是Promise對象的規(guī)則定義開始的。使用async/await結(jié)果和之前的 Promise 實(shí)現(xiàn)是一樣的,但是這個(gè)代碼看起來是不是清晰得多,幾乎跟同步代碼一樣。


05
注意事項(xiàng)


1、await只能用在async函數(shù)之中,也就是說await必須和async一起使用,反之,async可以單獨(dú)只用。


2、await后面跟著是一個(gè)Promise對象,會(huì)等待Promise返回結(jié)果了,再繼續(xù)執(zhí)行后面的代碼。

const timer = n => {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('1')
            resolve();
        }, n);
    })
};
const run = async () => {
    await timer(3000);
    console.log('2');
};
run()
// 等待3s之后,分別打印出1、2


3、await后面跟著的是一個(gè)數(shù)值或者是字符串等數(shù)據(jù)類型的值,則直接返回該值。


4、await后面跟著的是定時(shí)器,不會(huì)等待定時(shí)器里面的代碼執(zhí)行完,而是直接執(zhí)行后面的代碼,然后再執(zhí)行定時(shí)器中的代碼。

const run = async () => {
    console.log(1)
    await setTimeout(() => {
        console.log(2)
    }, 1000);
    console.log(3);
};
run()
// 1 -> 3 -> 2


5、可以直接用標(biāo)準(zhǔn)的try...catch...語法捕捉錯(cuò)誤。

async function asyncFunction() {
    throw new Error('Something went wrong');
}

const run = async() => {
    try {
        await asyncFunction();
    } catch (error) {
        console.error('Caught an error:', error);
    }
}
run()


06
總結(jié)


從回調(diào)地獄到Promise的鏈?zhǔn)秸{(diào)⽤到Generator函數(shù)的分步執(zhí)⾏再到async和await的⾃動(dòng)異步代碼同步化機(jī)制,經(jīng)歷了很多個(gè)年頭,所以⾯試中為什么經(jīng)常問到Promise,并且重點(diǎn)沿著Promise對象深⼊的挖掘去問你各種問題,主要是考察程序員對Promise對象本身以及他的發(fā)展歷程是否有深⼊的了解,同時(shí)也是在考察⾯試者對JavaScript的事件循環(huán)系統(tǒng)和異步編程的基本功是否⾜夠的扎實(shí)。Promise和事件循環(huán)系統(tǒng)并不是JavaScript中的⾼級知識,⽽是真正的基礎(chǔ)知識,所以所有⼈想要在⾏業(yè)中更好的發(fā)展下去,這些知識都是必備基礎(chǔ),必須扎實(shí)掌握。


該文章在 2024/4/1 15:05:16 編輯過
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購管理,倉儲(chǔ)管理,倉庫管理,保質(zhì)期管理,貨位管理,庫位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved