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

一篇文章詳解JS的四種異步解決方案

 更新時(shí)間:2024年06月20日 11:27:03   作者:chengbo_eva  
異步在JS中是常用的,下面這篇文章主要給大家介紹了關(guān)于JS四種異步解決方案的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用js具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

前言

「異步編程」是前端工程師日常開發(fā)中經(jīng)常會(huì)用到的技術(shù),也是校招面試過(guò)程中常考的一個(gè)知識(shí)點(diǎn)。

通過(guò)掌握「異步編程」的四種方式,可以讓我們能夠更好地處理JavaScript中的異步操作,提高代碼的性能和用戶體驗(yàn)。

因此,「今天就想和大家來(lái)聊聊JS異步編程的四種方式!」

同步&異步的概念

在講這四種異步方案之前,我們先來(lái)明確一下同步和異步的概念:

        所謂「同步(synchronization)」,簡(jiǎn)單來(lái)說(shuō),就是「順序執(zhí)行」,指的是同一時(shí)間只能做一件事情,只有目前正在執(zhí)行的事情做完之后,才能做下一件事情。

        「同步操作的優(yōu)點(diǎn)」在于做任何事情都是依次執(zhí)行,井然有序,不會(huì)存在大家同時(shí)搶一個(gè)資源的問(wèn)題。

        「同步操作的缺點(diǎn)」在于「會(huì)阻塞后續(xù)代碼的執(zhí)行」。如果當(dāng)前執(zhí)行的任務(wù)需要花費(fèi)很長(zhǎng)的時(shí)間,那么后面的程序就只能一直等待。

        所謂「異步(Asynchronization)」,指的是當(dāng)前代碼的執(zhí)行不影響后面代碼的執(zhí)行。當(dāng)程序運(yùn)行到異步的代碼時(shí),會(huì)將該異步的代碼作為任務(wù)放進(jìn)「任務(wù)隊(duì)列」,而不是推入主線程的調(diào)用棧。等主線程執(zhí)行完之后,再去任務(wù)隊(duì)列里執(zhí)行對(duì)應(yīng)的任務(wù)即可。

        因此,「異步操作的優(yōu)點(diǎn)就是:不會(huì)阻塞后續(xù)代碼的執(zhí)行?!?/strong>

js中異步的應(yīng)用場(chǎng)景

開篇講了同步和異步的概念,那么在JS中異步的應(yīng)用場(chǎng)景有哪些呢?

  • 「定時(shí)任務(wù)」:setTimeout、setInterval

  • 「網(wǎng)絡(luò)請(qǐng)求」:ajax請(qǐng)求、動(dòng)態(tài)創(chuàng)建img標(biāo)簽的加載

  • 「事件監(jiān)聽器」:addEventListener

實(shí)現(xiàn)異步的四種方法

對(duì)于「setTimeout、setInterval、addEventListener」這種異步場(chǎng)景,不需要我們手動(dòng)實(shí)現(xiàn)異步,直接調(diào)用即可。

但是對(duì)于「ajax請(qǐng)求」、「node.js中操作數(shù)據(jù)庫(kù)這種異步」,就需要我們自己來(lái)實(shí)現(xiàn)了~

1、 回調(diào)函數(shù)

在微任務(wù)隊(duì)列出現(xiàn)之前,JS實(shí)現(xiàn)異步的主要方式就是通過(guò)「回調(diào)函數(shù)」。

以一個(gè)簡(jiǎn)易版的Ajax請(qǐng)求為例,代碼結(jié)構(gòu)如下所示:

function ajax(obj){
 let default = {
   url: '...',
   type:'GET',
   async:true,
   contentType: 'application/json',
   success:function(){}
    };

 for (let key in obj) {
        defaultParam[key] = obj[key];
    }

    let xhr;
    if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
    } else {
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    
    xhr.open(defaultParam.type, defaultParam.url+'?'+dataStr, defaultParam.async);
    xhr.send();
    xhr.onreadystatechange = function (){
        if (xhr.readyState === 4){
            if(xhr.status === 200){
                let result = JSON.parse(xhr.responseText);
                // 在此處調(diào)用回調(diào)函數(shù)
                defaultParam.success(result);
            }
        }
    }
}

我們?cè)跇I(yè)務(wù)代碼里可以這樣調(diào)用「ajax請(qǐng)求」

ajax({
   url:'#',
   type:GET,
   success:function(e){
    // 回調(diào)函數(shù)里就是對(duì)請(qǐng)求結(jié)果的處理
   }
});

「ajax請(qǐng)求」中的success方法就是一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)中執(zhí)行的是我們請(qǐng)求成功之后要做的進(jìn)一步操作。

這樣就初步實(shí)現(xiàn)了異步,但是回調(diào)函數(shù)有一個(gè)非常嚴(yán)重的缺點(diǎn),那就是「回調(diào)地獄」的問(wèn)題。

大家可以試想一下,如果我們?cè)诨卣{(diào)函數(shù)里再發(fā)起一個(gè)ajax請(qǐng)求呢?那豈不是要在success函數(shù)里繼續(xù)寫一個(gè)ajax請(qǐng)求?那如果需要多級(jí)嵌套發(fā)起ajax請(qǐng)求呢?豈不是需要多級(jí)嵌套?

如果嵌套的層級(jí)很深的話,我們的代碼結(jié)構(gòu)可能就會(huì)變成這樣:

因此,為了解決回調(diào)地獄的問(wèn)題,提出了「promise」、「async/await」「generator」的概念。

2、Promise

「Promise」作為典型的微任務(wù)之一,它的出現(xiàn)可以使JS達(dá)到異步執(zhí)行的效果。

一個(gè)「Promise函數(shù)」的結(jié)構(gòu)如下列代碼如下:

const promise = new Promise((resolve, reject) => {
 resolve('a');
});
promise
    .then((arg) => { console.log(`執(zhí)行resolve,參數(shù)是${arg}`) })
    .catch((arg) => { console.log(`執(zhí)行reject,參數(shù)是${arg}`) })
    .finally(() => { console.log('結(jié)束promise') });

如果我們需要嵌套執(zhí)行異步代碼,相比于回調(diào)函數(shù)來(lái)說(shuō),「Promise」的執(zhí)行方式如下列代碼所示:

const promise = new Promise((resolve, reject) => {
 resolve(1);
});
promise.then((value) => {
     console.log(value);
     return value * 2;
    }).then((value) => {
     console.log(value);
     return value * 2;
    }).then((value) => {
    console.log(value);
    }).catch((err) => {
  console.log(err);
    });

即通過(guò)then來(lái)實(shí)現(xiàn)多級(jí)嵌套(「鏈?zhǔn)秸{(diào)用」),這看起來(lái)是不是就比回調(diào)函數(shù)舒服多了~

每個(gè)「Promise」都會(huì)經(jīng)歷的生命周期是:

  • 進(jìn)行中(pending) :此時(shí)代碼執(zhí)行尚未結(jié)束,所以也叫未處理的(unsettled)

    已處理(settled) :異步代碼已執(zhí)行結(jié)束 已處理的代碼會(huì)進(jìn)入兩種狀態(tài)中的一種:
    • 已拒絕(rejected):遇到錯(cuò)誤,異步代碼執(zhí)行失敗 ,由reject()觸發(fā)

    • 已完成(fulfilled):表明異步代碼執(zhí)行成功,由resolve()觸發(fā)

因此,「pending」「fulfilled」「rejected」就是「Promise」中的三種狀態(tài)啦~

        需要注意的是,在「Promise」中,要么包含resolve() 來(lái)表示 「Promise」 的狀態(tài)為fulfilled,要么包含 reject() 來(lái)表示「Promise」的狀態(tài)為rejected。

        不然我們的「Promise」就會(huì)一直處于pending的狀態(tài),直至程序崩潰...

除此之外,「Promise」不僅很好的解決了鏈?zhǔn)秸{(diào)用的問(wèn)題,它還有很多高頻的操作:

  • ·Promise.all(promises) :接收一個(gè)包含多個(gè)Promise對(duì)象的數(shù)組,等待所有都完成時(shí),返回存放它們結(jié)果的數(shù)組。如果任一被拒絕,則立即拋出錯(cuò)誤,其他已完成的結(jié)果會(huì)被忽略

  • ·Promise.allSettled(promises) : 接收一個(gè)包含多個(gè)Promise對(duì)象的數(shù)組,等待所有都已完成或者已拒絕時(shí),返回存放它們結(jié)果對(duì)象的數(shù)組。每個(gè)結(jié)果對(duì)象的結(jié)構(gòu)為{status:'fulfilled' // 或 'rejected', value // 或reason}

  • ·Promise.race(promises) : 接收一個(gè)包含多個(gè)Promise對(duì)象的數(shù)組,等待第一個(gè)有結(jié)果(完成/拒絕)的Promise,并把其result/error作為結(jié)果返回

示例代碼如下所示:

function getPromises(){
    return [
        new Promise(((resolve, reject) => setTimeout(() => resolve(1), 1000))),
        new Promise(((resolve, reject) => setTimeout(() => reject(new Error('2')), 2000))),
        new Promise(((resolve, reject) => setTimeout(() => resolve(3), 3000))),
    ];
}

Promise.all(getPromises()).then(console.log);
Promise.allSettled(getPromises()).then(console.log);
Promise.race(getPromises()).then(console.log);

打印結(jié)果為:

3、Generator

「generator」是ES6提出的一種異步編程的方案。因?yàn)槭謩?dòng)創(chuàng)建一個(gè)iterator十分麻煩,因此ES6推出了「generator」,用于更方便的創(chuàng)建iterator。

也就是說(shuō),「generator」就是一個(gè)返回值為iterator對(duì)象的函數(shù)。

在講「generator」之前,我們先來(lái)看看iterator是什么:

?

iterator中文名叫「迭代器」。它為js中各種不同的數(shù)據(jù)結(jié)構(gòu)(Object、Array、Set、Map)提供統(tǒng)一的訪問(wèn)機(jī)制。
任何數(shù)據(jù)結(jié)構(gòu)只要部署iterator接口,就可以完成遍歷操作。
因此iterator也是一種對(duì)象,不過(guò)相比于普通對(duì)象來(lái)說(shuō),它有著專為迭代而設(shè)計(jì)的接口。

?

我們通過(guò)一個(gè)例子來(lái)看看generator的特征:

function* createIterator() {
  yield 1;
  yield 2;
  yield 3;
}
// generators可以像正常函數(shù)一樣被調(diào)用,不同的是會(huì)返回一個(gè) iterator
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

形式上,「generator」 函數(shù)是一個(gè)普通函數(shù),但是有兩個(gè)特征:

  • ·function關(guān)鍵字與函數(shù)名之間有一個(gè)星號(hào)

  • ·函數(shù)體內(nèi)部使用yield語(yǔ)句,定義不同的內(nèi)部狀態(tài)

在普通函數(shù)中,我們想要一個(gè)函數(shù)最終的執(zhí)行結(jié)果,一般都是return出來(lái),或者以return作為結(jié)束函數(shù)的標(biāo)準(zhǔn)。運(yùn)行函數(shù)時(shí)也不能被打斷,期間也不能從外部再傳入值到函數(shù)體內(nèi)。

但在「generator」中,就打破了這幾點(diǎn),所以「generator」和普通的函數(shù)完全不同。

當(dāng)以function*  的方式聲明了一個(gè)「generator」生成器時(shí),內(nèi)部是可以有許多狀態(tài)的,以yield進(jìn)行斷點(diǎn)間隔。期間我們執(zhí)行調(diào)用這個(gè)生成的「generator」,他會(huì)返回一個(gè)遍歷器對(duì)象,用這個(gè)對(duì)象上的方法,實(shí)現(xiàn)獲得一個(gè)yield后面輸出的結(jié)果。

function* generator() {
    yield 1
    yield 2
};
let iterator = generator();
iterator.next()  // {value: 1, done: false}
iterator.next()  // {value: 2, done: false}
iterator.next()  // {value: undefined, done: true}

4、 async/await

最后我們來(lái)講講「async/await」,終于講到這兒了?。?!

「async/await」是ES7提出的關(guān)于異步的終極解決方案。我看網(wǎng)上關(guān)于「async/await」是誰(shuí)的語(yǔ)法糖這塊有兩個(gè)版本:

  • 第一個(gè)版本說(shuō)「async/await」是Generator的語(yǔ)法糖

  • 第二個(gè)版本說(shuō)「async/await」是Promise的語(yǔ)法糖

其實(shí),這兩種說(shuō)法都沒(méi)有錯(cuò)。

「關(guān)于async/await是Generator的語(yǔ)法糖:」

 所謂generator語(yǔ)法糖,表明的就是「aysnc/await」實(shí)現(xiàn)的就是generator實(shí)現(xiàn)的功能。但是「async/await」比generator要好用。因?yàn)間enerator執(zhí)行yield設(shè)下的斷點(diǎn)采用的方式就是不斷的調(diào)用iterator方法,這是個(gè)手動(dòng)調(diào)用的過(guò)程。

而async配合await得到的就是斷點(diǎn)執(zhí)行后的結(jié)果。因此「async/await」比generator使用更普遍。

「關(guān)于async/await是Promise的語(yǔ)法糖:」

如果不使用「async/await」的話,Promise就需要通過(guò)鏈?zhǔn)秸{(diào)用來(lái)依次執(zhí)行then之后的代碼:

function counter(n){
    return new Promise((resolve, reject) => { 
        resolve(n + 1);
    });
}

function adder(a, b){
    return new Promise((resolve, reject) => { 
        resolve(a + b);
    });
}

function delay(a){
    return new Promise((resolve, reject) => { 
        setTimeout(() => resolve(a), 1000);
    });
}
// 鏈?zhǔn)秸{(diào)用寫法
function callAll(){
    counter(1)
       .then((val) => adder(val, 3))
       .then((val) => delay(val))
       .then(console.log);
}
callAll();//5

雖然相比于回調(diào)地獄來(lái)說(shuō),鏈?zhǔn)秸{(diào)用確實(shí)順眼多了。但是其呈現(xiàn)仍然略繁瑣了一些。

「async/await的出現(xiàn),就使得我們可以通過(guò)同步代碼來(lái)達(dá)到異步的效果」

async function callAll(){
   const count = await counter(1);
   const sum = await adder(count, 3);
   console.log(await delay(sum));
}
callAll();// 5

由此可見(jiàn),「Promise搭配async/await的使用才是正解!」

總結(jié)

到此這篇關(guān)于JS四種異步解決方案的文章就介紹到這了,更多相關(guān)JS異步解決方案內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論