欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入探究JS中的異步編程和事件循環(huán)機(jī)制

 更新時(shí)間:2023年05月23日 10:33:06   作者:阿托  
js是單線程事件循環(huán)模型,同步操作與異步操作時(shí)代碼所依賴的核心機(jī)制,異步行為是為了優(yōu)化因計(jì)算量大而時(shí)間長(zhǎng)的操作,本文詳細(xì)給大家介紹了JS中的異步編程和事件循環(huán)機(jī)制,文中有詳細(xì)的代碼示例,需要的朋友可以參考下

異步編程

js是單線程事件循環(huán)模型,同步操作與異步操作時(shí)代碼所依賴的核心機(jī)制。異步行為是為了優(yōu)化因計(jì)算量大而時(shí)間長(zhǎng)的操作。

同步:當(dāng)一個(gè)進(jìn)程在執(zhí)行某個(gè)請(qǐng)求時(shí),如果這個(gè)請(qǐng)求需要等待一段時(shí)間才能返回,那么這個(gè)進(jìn)程會(huì)一直等下去,直到請(qǐng)求返回。

異步:當(dāng)一個(gè)進(jìn)程在執(zhí)行某個(gè)請(qǐng)求時(shí),如果這個(gè)請(qǐng)求需要等待一段時(shí)間才能返回,那么這個(gè)進(jìn)程會(huì)直接向下執(zhí)行,不需要等待,當(dāng)消息返回時(shí)再通知進(jìn)程處理。

Promise

Promise的出現(xiàn)主要時(shí)解決之間異步編程使用回調(diào)函數(shù)的回調(diào)地獄的問題。

Promise的狀態(tài)

Promise有三種狀態(tài):

  • pending:待定
  • fulfilled:成功
  • rejected:失敗

兩種狀態(tài)變化:

  • pending -> fulfilled:在promise中調(diào)用resolve()函數(shù)會(huì)產(chǎn)生此狀態(tài)轉(zhuǎn)變
  • pending ->rejected:在promise中調(diào)用reject()函數(shù)會(huì)產(chǎn)生此狀態(tài)轉(zhuǎn)變

promise的基本流程

Promise構(gòu)造函數(shù)

用于創(chuàng)建Promise對(duì)象,需要傳入一個(gè)執(zhí)行器函數(shù),函數(shù)中需要帶兩個(gè)參數(shù):resolved,rejected;用于修改Promise的狀態(tài)

let p = new Promise((resolved,rejected)=>{
    console.log(1);
    resolved();
})
// 1
console.log(p); //Promise?{<fulfilled>: undefined}

Promise.prototype.then()

用于定義成功或失敗狀態(tài)的回調(diào)函數(shù),并返回一個(gè)新的Promise。

let p = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
        resolved("ok");
    },1000);
});
p.then(res=>{
    console.log(res);
},err=>{
    console.log(err);
})
// ok
let p = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
        rejected("err");
    },1000);
});
p.then(res=>{
    console.log(res);
},err=>{
    console.log(err);
})
// err

Promise.prototypr.catch()

用于定義失敗的回調(diào),then方法的語法糖,相當(dāng)于.then(undefined,onRejected);

let p = new Promise((resolved,rejected)=>{
    setTimeout(()=>{
        rejected("err");
    },1000);
});
p.then(res=>{
    console.log(res);
}).catch(err=>{
    console.log(err);
})
// err

Promise.resolve()

返回一個(gè)成功或者失敗的Promise;

let p = Promise.resolve("ok"); //Promise?{<fulfilled>: ok}
let p = Promise.resolve(Promise.reject('err')); //Promise?{<rejected>: 'err'}

Promise.all()

返回一個(gè)新的Promise,只有所有的Promise都成功才成功,否則有一個(gè)失敗就返回失敗的promise

let p = Promise.all([
    Promise.resolve(1),
    Promise.resolve(2),
	Promise.resolve()
]) // Promise?{<fulfilled>: [1,2,undefined]} 
let p = Promise.all([
    Promise.reject('err'),
    Promise.resolve(1),
    Promise.resolve(2)
]) // Promise?{<rejected>: 'err'}

Promise.race()

返回一個(gè)新的Promise,第一個(gè)完成的Promise的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)。

let p = Promise.race([
    Promise.resolve(1),
    Promise.resolve(2),
	Promise.resolve()
]) // Promise?{<fulfilled>: 1} 
let p = Promise.all([
    Promise.reject('err'),
    Promise.resolve(1),
    Promise.resolve(2)
]) // Promise?{<rejected>: 'err'}

Promise原理實(shí)現(xiàn)

function Promise(excutor){
    const self = this;
    self.status = "pending";
    self.data = undefined;
    self.callbacks = [] // 用來保存所有待調(diào)用的回調(diào)函數(shù)
    function resolve(value){
        if(self.status !== "pending"){
            return;
        }
        // 改變狀態(tài)
        self.status = "fulfilled";
        self.data = value;
        //  異步調(diào)用回調(diào)函數(shù)
        if(self.callbacks.length > 0){
            setTimeout(()=>{
                self.callbacks.forEach((obj)=>{
                    obj.onResolved();
                })
            })
        }
    }
    function reject(reason){
        if(self.status !== "pending"){
            return;
        }
        // 改變狀態(tài)
        self.status = "rejected";
        self.data = value;
        //  異步調(diào)用回調(diào)函數(shù)
        if(self.callbacks.length > 0){
            setTimeout(()=>{
                self.callbacks.forEach((obj)=>{
                    obj.onRejected();
                })
            })
        }
    }
    // 立即同步調(diào)用執(zhí)行器函數(shù)
    try{
        excutor(resolve,reject);
    }catch(error){
        reject(error);
    }
}
Promise.prototype.then = function(onResolved,onRejected){
    const  self = this;
    onResolved = typeof onResolved === 'function'?onResolved:value=>value;
    onRejected = typeof onResolved === 'function'?onRejected:reason=>{throw reason};
    // 返回promise對(duì)象
    return new Promise((resolve,reject)=>{
        // 執(zhí)行回調(diào)函數(shù)
        function handle(callback){
            try{
                const x = callback(self.data);
                // 如果返回對(duì)象為promise
                if(x instanceof Promise){
                    x.then(resolve,reject);
                }else{
                    resolve(x);
                }
            }catch(err){
                reject(err);
            }
        }
        // 定義回調(diào)之前,狀態(tài)已經(jīng)改變
        if(self.status == 'fulfilled'){
            setTimeout(()=>{
                handle(onResolved);
            })
        }else if(self.status == 'rejected'){
            setTimeout(()=>{
                handle(onRejected);
            })
        }else{
            // 定義回調(diào)之前狀態(tài)還未改變
            self.callbacks.push({
                onResolved(){
                    handle(onResolved);
                },
                onRejected(){
                    handle(onRejected);
                }
            })
        }
    })
}
Promise.prototype.catch = function(onRejected){
    return this.then(undefined,onRejected)
}
Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{
        if(value instanceof Promise){
            value.then(resolve,reject);
        }else{
            resolve(value);
        }
    })
}
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason);
    })
}
Promise.all = function(promises){
    return new Promise((resolve,reject)=>{
        let count = 0;
        let plen = promises.length;
        let values = new Array(plen);
        for(let i = 0;i < plen;i++){
            Promise.resolve(promises[i]).then(value=>{
                count++;
                value[i] = value;
                if(count == plen) resolve(values);
            },err=>{
                reject(err);
            })
        }
    })
}
Promise.race = function(promises){
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < promises.length;i++){
            Promise.resolve(promises[i]).then(value=>{
                resolve(value);
            },err=>{
                reject(err);
            })
        }
    })
}

異步函數(shù)async/await

ES8提出使用async和await解決異步結(jié)構(gòu)組織代碼的問題

async

async關(guān)鍵字用于聲明異步函數(shù),使用async關(guān)鍵字可以讓函數(shù)具有異步特征,但總體上其代碼仍然時(shí)同步求值。

async function fn1(){
    console.log(1);
}
fn1();
console.log(2);
// 1
// 2

async的返回值是一個(gè)Promise對(duì)象,如果函數(shù)中返回一個(gè)值value,則async則返回Promise.resolve(value);

await

因?yàn)楫惒胶瘮?shù)主要針對(duì)不會(huì)馬上完成的任務(wù),所以自然需要一種暫停和恢復(fù)執(zhí)行的能力。

await關(guān)鍵字必須在異步函數(shù)中使用。

async/await中真正起作用的是await。async關(guān)鍵字,無論從哪個(gè)方面來看,都是一個(gè)標(biāo)識(shí)符。異步函數(shù)中不包含await關(guān)鍵字,其執(zhí)行基本上跟普通函數(shù)沒有區(qū)別。

async function fn(){
    console.log(1);
    let p = await Promise.resolve('ok');
    console.log(p);
    console.log(2);
}
fn();
console.log(3);
// 1
// 3
// ok
// 2

js事件循環(huán)機(jī)制

js運(yùn)行機(jī)制

因?yàn)閖s時(shí)單線程的,在執(zhí)行代碼時(shí),將不同函數(shù)的執(zhí)行上下文壓入棧中來保證代碼的有序執(zhí)行,在執(zhí)行同步代碼的時(shí)候,如果遇到異步代碼,js并不會(huì)等待其返回結(jié)果,而是將異步代碼交給對(duì)應(yīng)的異步代碼處理線程處理,繼續(xù)執(zhí)行棧中的任務(wù)。

當(dāng)異步事件執(zhí)行完畢之后,再將異步事件對(duì)應(yīng)的對(duì)調(diào)加入異步執(zhí)行隊(duì)列,當(dāng)主棧中的函數(shù)執(zhí)行完畢后,會(huì)從異步執(zhí)行隊(duì)列中選擇任務(wù)來執(zhí)行。

宏任務(wù)和微任務(wù)

在任務(wù)隊(duì)列中異步回調(diào)分為2中:微任務(wù)和宏任務(wù)。也就是說任務(wù)隊(duì)列可以分為兩種:微任務(wù)隊(duì)列和宏任務(wù)隊(duì)列。

宏任務(wù):全局代碼,settimeout/setinterval,UI渲染,

微任務(wù):promise.then,catch,finally,process.nextTick

宏任務(wù)和微任務(wù)的執(zhí)行順序

  • 代碼從開始執(zhí)行調(diào)用全局執(zhí)行棧,script標(biāo)簽內(nèi)的代碼做為宏任務(wù)執(zhí)行。

  • 執(zhí)行過程中同步代碼立即執(zhí)行,異步代碼由異步處理線程處理然后放入任務(wù)隊(duì)列中。

  • 執(zhí)行過程中同步代碼立即執(zhí)行,異步代碼放入任務(wù)隊(duì)列中。

    • 先看任務(wù)隊(duì)列中的微任務(wù)隊(duì)列是否存在微任務(wù)

      有微任務(wù):執(zhí)行微任務(wù)隊(duì)列中的所有微任務(wù)

      微任務(wù)執(zhí)行過程中所產(chǎn)生的微任務(wù)放到微任務(wù)隊(duì)列中,繼續(xù)執(zhí)行。

    • 如果沒微任務(wù),查看是否具有宏任務(wù),有的話執(zhí)行,沒有的話事件輪詢結(jié)束。

      執(zhí)行過程中所產(chǎn)生的微任務(wù)放到微任務(wù)隊(duì)列中。

      完成單個(gè)宏任務(wù)之后,執(zhí)行微任務(wù)隊(duì)列中的任務(wù)。

常見面試題

異步編程的實(shí)現(xiàn)方式有哪些

  • 回調(diào)函數(shù):使用回調(diào)函數(shù)的方式的缺點(diǎn)是,是個(gè)回調(diào)函數(shù)嵌套的時(shí)候,會(huì)照成回調(diào)函數(shù)地獄,上下兩層的回調(diào)函數(shù)間的代碼耦合度太高,不利于代碼維護(hù)。
  • promise:使用promise的方式可以嵌套的函數(shù)調(diào)用做為鏈?zhǔn)秸{(diào)用,但是使用這種方法,有時(shí)會(huì)找出多個(gè)then的鏈?zhǔn)秸{(diào)用,可能造成代碼的語義不夠明確。
  • generator:配合yield實(shí)現(xiàn),可以在函數(shù)內(nèi)實(shí)現(xiàn)中斷。
  • async/await:async函數(shù)是generator和promise實(shí)現(xiàn)的一個(gè)自動(dòng)執(zhí)行的語法糖,他內(nèi)部自帶執(zhí)行器,當(dāng)函數(shù)內(nèi)部執(zhí)行到await語句的時(shí)候,如果語句返回promise對(duì)象,那么函數(shù)將會(huì)等待promise對(duì)象的狀態(tài)變?yōu)閞esolve后再基于向下執(zhí)行。因此可以將異步邏輯轉(zhuǎn)化為同步的順序來寫,并且這個(gè)函數(shù)可以自動(dòng)執(zhí)行。

如何改變promise的狀態(tài)

  • 調(diào)用resolve(value),pending ->fulfilled
  • 調(diào)用reject(reason),pending ->rejected
  • 拋出異常:如果當(dāng)前時(shí)pending就會(huì)變成rejected

改變promise狀態(tài)和指定回調(diào)函數(shù)誰先誰后

都有可能,正常情況下是先指定回調(diào)再改變狀態(tài),但也可以先改變狀態(tài)再指定回調(diào)。

什么時(shí)候能得到數(shù)據(jù)?

  • 先指定回調(diào),當(dāng)狀態(tài)發(fā)生改變時(shí),回調(diào)函數(shù)就會(huì)調(diào)用,得到數(shù)據(jù)。
  • 先改變狀態(tài),當(dāng)指定回調(diào)函數(shù)時(shí),回調(diào)函數(shù)就會(huì)調(diào)用,得到數(shù)據(jù)。

promise中改變狀態(tài)和指定回調(diào)函數(shù)時(shí)做了什么事情

在promise內(nèi)部定義了一個(gè)回調(diào)隊(duì)列。

在狀態(tài)改變時(shí),也就是調(diào)用onresolved或者onrejected函數(shù)時(shí),會(huì)執(zhí)行回調(diào)隊(duì)列中的函數(shù)。

當(dāng)指定回調(diào)函數(shù)時(shí),首先會(huì)判斷當(dāng)前的狀態(tài)是否是pending,如果是則將回調(diào)函數(shù)加入回調(diào)隊(duì)列,否則直接執(zhí)行根據(jù)狀態(tài)執(zhí)行onresolved或者onrejected函數(shù)。

這就是為什么無論是先改變狀態(tài)還是先指定回調(diào)函數(shù),都能得到最終的結(jié)果的原因。

promise.then() 返回的新promise的結(jié)果由什么決定

  • 如果拋出異常,新的promise變?yōu)閞eject,reason為拋出的異常

  • 如果返回的時(shí)非promise的任意值,新promise變?yōu)閒ulfilled,value為返回的值

  • 如果返回promise的時(shí)另一個(gè)promise對(duì)象,此peomise的結(jié)果就會(huì)成為新promise的結(jié)果。

promise異常穿透

  • 當(dāng)使用promise的then鏈?zhǔn)秸{(diào)用時(shí),可以在最后指定失敗的回調(diào)
  • 前面任何操作處理異常都會(huì)傳到最后的回調(diào)中處理

中斷promise鏈

當(dāng)使用promise的then鏈?zhǔn)秸{(diào)用,在中間中斷,不在調(diào)用后面的回調(diào)函數(shù)。

辦法:在回調(diào)函數(shù)中返回一個(gè)pending狀態(tài)的promise對(duì)象

async/await對(duì)比promise的優(yōu)勢(shì)

  • 代碼讀起來更像同步,Promise雖然擺脫了回調(diào)地獄,但是then方法的鏈?zhǔn)秸{(diào)用也會(huì)帶來額外的閱讀負(fù)擔(dān)
  • Promise傳遞中間值非常麻煩,而async、await幾乎是同步的寫法

Promist.catch后面的.then還會(huì)執(zhí)行嗎

.then回執(zhí)行,因?yàn)閏atch方法返回的還是一個(gè)promise對(duì)象,依然支持鏈?zhǔn)秸{(diào)用。

Promise中,resolve后面的語句是否還會(huì)執(zhí)行?

會(huì)被執(zhí)行。如果不需要執(zhí)行,需要在 resolve 語句前加上 return。

JS為什么是單線程語言

JavaScript的誕生就是為了處理瀏覽器網(wǎng)頁的交互(DOM操作的處理、UI動(dòng)畫等), 設(shè)計(jì)成單線程的原因就是不想讓瀏覽器變得太復(fù)雜,因?yàn)槎嗑€程需要共享資源、且有可能修改彼此的運(yùn)行結(jié)果(兩個(gè)線程修改了同一個(gè)DOM節(jié)點(diǎn)就會(huì)產(chǎn)生不必要的麻煩),這對(duì)于一種網(wǎng)頁腳本語言來說這就太復(fù)雜了。

為了利用多核CPU的計(jì)算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個(gè)新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì)。

JS是怎么實(shí)現(xiàn)異步異常運(yùn)行的

JavaScript是單線程的,但它所運(yùn)行的宿主環(huán)境—瀏覽器是多線程,瀏覽器提供了各種線程供Event Loop調(diào)度來協(xié)調(diào)JS單線程運(yùn)行時(shí)不會(huì)阻塞

談一談setInterval的問題

setTimeout的作用是每隔一段指定事件執(zhí)行一個(gè)函數(shù),但是這個(gè)執(zhí)行不是真的到了時(shí)間立即執(zhí)行,它真正的作用是每隔一段時(shí)間將事件加入事件隊(duì)列中去,只有當(dāng)當(dāng)前的執(zhí)行棧為空的時(shí)候,才能去從事件隊(duì)列中取出事件執(zhí)行。

所以可能會(huì)出現(xiàn)這樣的情況,就是當(dāng)前執(zhí)行棧執(zhí)行的時(shí)間很長(zhǎng),導(dǎo)致事件隊(duì)列里邊積累多個(gè)定時(shí)器加入的事件,當(dāng)執(zhí)行棧結(jié)束的時(shí)候,這些事件會(huì)依次執(zhí)行,因此就不能到間隔一段時(shí)間執(zhí)行的效果。

正對(duì)這種現(xiàn)象,我們可以使用setTimeout遞歸調(diào)用來模擬實(shí)現(xiàn)setInterval,這樣就能確保了只有一個(gè)事件結(jié)束,我們才會(huì)觸發(fā)下一個(gè)定時(shí)器事件。

function mySetInterval(fn,delay){
    var timer = {
        flag = true;
    };
    function interval(){
        if(timer.flag){
            fn();
            setTimeout(interval,delay);
        }
    }
    setTimeout(intetval,delay);
    return timer;
}
function myClearInterval(timer){
    timer.flag = false;
}

以上就是深入探究JS中的異步編程和事件循環(huán)機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于JS異步編程和事件循環(huán)機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JS格式化時(shí)間的幾種方法總結(jié)

    JS格式化時(shí)間的幾種方法總結(jié)

    這篇文章介紹了JS格式化時(shí)間的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • 淺談TypeScript 用 Webpack/ts-node 運(yùn)行的配置記錄

    淺談TypeScript 用 Webpack/ts-node 運(yùn)行的配置記錄

    這篇文章主要介紹了淺談TypeScript 用 Webpack/ts-node 運(yùn)行的配置記錄,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • JS打印彩色菱形的實(shí)例代碼

    JS打印彩色菱形的實(shí)例代碼

    本文通過一段簡(jiǎn)單的實(shí)例代碼給大家介紹js實(shí)現(xiàn)打印彩色菱形的方法,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2018-08-08
  • 微信小程序?qū)崿F(xiàn)帶刻度尺滑塊功能

    微信小程序?qū)崿F(xiàn)帶刻度尺滑塊功能

    這篇文章主要介紹了微信小程序?qū)崿F(xiàn)帶刻度尺滑塊功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-03-03
  • js中style.display=

    js中style.display=""無效的解決方法

    這篇文章主要介紹了js中style.display=""無效的解決方法,是js程序設(shè)計(jì)中非常常見的問題,需要的朋友可以參考下
    2014-10-10
  • 使用JavaScript實(shí)現(xiàn)表格編輯器(實(shí)例講解)

    使用JavaScript實(shí)現(xiàn)表格編輯器(實(shí)例講解)

    下面小編就為大家?guī)硪黄褂肑avaScript實(shí)現(xiàn)表格編輯器(實(shí)例講解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • 使用JavaScript實(shí)現(xiàn)node.js中的path.join方法

    使用JavaScript實(shí)現(xiàn)node.js中的path.join方法

    Node.JS中的 path.join 非常方便,能直接按相對(duì)或絕對(duì)合并路徑,有時(shí)侯前端也需要這種方法,如何實(shí)現(xiàn)呢?感興趣的朋友跟隨腳本之家小編一起看看吧
    2018-08-08
  • JS實(shí)現(xiàn)分頁瀏覽橫向圖片(類輪播)實(shí)例代碼

    JS實(shí)現(xiàn)分頁瀏覽橫向圖片(類輪播)實(shí)例代碼

    這篇文章主要介紹了JS實(shí)現(xiàn)分頁瀏覽橫向圖片(類輪播)實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-11-11
  • JavaScript Reduce使用詳解

    JavaScript Reduce使用詳解

    這篇文章主要介紹了JavaScript Reduce使用的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用JavaScript,感興趣的朋友可以了解下
    2021-03-03
  • javascript實(shí)現(xiàn)在網(wǎng)頁中運(yùn)行本地程序的方法

    javascript實(shí)現(xiàn)在網(wǎng)頁中運(yùn)行本地程序的方法

    這篇文章主要介紹了javascript實(shí)現(xiàn)在網(wǎng)頁中運(yùn)行本地程序的方法,實(shí)例分析了JavaScript基于ActiveXObject運(yùn)行本地程序的技巧,需要的朋友可以參考下
    2016-02-02

最新評(píng)論