日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

實(shí)操ES6之Promise

 Coder編程 2021-09-20

箭頭函數(shù)和this

寫Promise的時候,自然而然會使用箭頭函數(shù)的編寫方式。箭頭函數(shù)就是.Neter們熟知的lambda函數(shù),已經(jīng)被大部分主流語言支持,也受到了廣大碼農(nóng)的交口稱贊,但是Jser們卻會遇到不大不小的一個坑。

眾所周知,js函數(shù)中的this由調(diào)用它的上下文決定,我們還可以通過apply、callbind等方式綁定上下文,從而改變函數(shù)中this的運(yùn)行時指向。而當(dāng)this遇到lambda時,又有所不同。

function Test() {
    this.name = "alice"
}

Test.prototype = {
    normal: function () {
        console.info(this.name)
    },
    lambda: () => {
        console.info(this.name)
    }
}

let test = new Test()
test.normal() //輸出:alice
test.lambda() //輸出:undefined

為什么會這樣?網(wǎng)上很多分析,讓人云里霧里。其實(shí)只要了解了——lambda與普通方法一個主要區(qū)別就是它能保持外層上下文變量的引用——這個特性就明白了。用lambda在方法內(nèi)寫過嵌套局部方法的.Neter很容易理解這個說法。

        private Action Test()
        {
            var name = "alice";            
            Action action = () => Console.WriteLine(name);
            //name將被捕獲,會一直生存到action被回收
            return action;
        }

so,可以將js的箭頭函數(shù)理解為受運(yùn)行時外層影響的內(nèi)嵌函數(shù),它是在運(yùn)行時賦值的——以上述js代碼為例,js解釋器解釋Test.prototype的定義,解釋到normal函數(shù)時,是不care其內(nèi)部邏輯的,繼續(xù)往下解釋到lambda函數(shù)時,會過一遍其內(nèi)部引用到的外部變量,若有則捕獲用于真正執(zhí)行時(所謂詞法作用域)。此時這個this指的是運(yùn)行環(huán)境的根對象(在瀏覽器中可能就是window對象),而不是test對象(此時還不存在噻)。注:本段為個人理解。

再看一個代碼片段,請讀者自行嘗試分析下:

var alice = {
    age: 18,
    getAge: function () {
        var aliceAge = this.age;//this是alice
        var getAgeWithLambda = () => this.age;//this還是alice
        var getAgeWithFunction = function () {
            return this.age;// this是window
        }
        return[aliceAge,getAgeWithLambda(),getAgeWithFunction()]
    }
}

console.info(alice.getAge()) //輸出:[18, 18, undefined]

Promise

Promise主要是將原本的callback變?yōu)?code>then,寫出來的代碼更便于閱讀。有多種方式得到一個Promise對象:

  1. Promise.resolve()Promise.resolve('foo') 等價于 new Promise(resolve => resolve('foo'))
  2. 執(zhí)行async修飾的函數(shù):
async function newPromise(){}
let p = newPromise() //p就是Promise對象

如果async函數(shù)中是return一個值,這個值就是Promise對象中resolve的值;如果async函數(shù)中是throw一個值,這個值就是Promise對象中reject的值。

  1. 直接構(gòu)造:
let p = new Promise((resolve, reject)=>{})

注意,構(gòu)造Promise時內(nèi)部代碼已經(jīng)開始執(zhí)行,只是把resolve部分掛起放到后面執(zhí)行。測試代碼如下:

let p = new Promise((resolve, _) => {
    resolve(1);
    console.info(2); //率先執(zhí)行
});
console.info(3);
p.then(num => {
    console.info(num); //后置執(zhí)行
});
console.info(4);

//輸出:2 3 4 1

所以,這跟慣常認(rèn)為的整個Promise代碼塊都后置執(zhí)行不一樣,需要注意。

我們可以如上述將回調(diào)邏輯寫在then里,也可以將邏輯移到外層變?yōu)橥綀?zhí)行(而非后置執(zhí)行),這就需要用到await關(guān)鍵字了,它將阻塞當(dāng)前代碼塊,等待resolve塊執(zhí)行完再往后執(zhí)行。代碼如下:

async function test() {
    let p = new Promise((resolve, _) => {
        resolve(1);
        console.info(2);
    });
    console.info(3);
    let num = await p;
    console.info(num);
    console.info(4);
}

test()

//輸出:2 3 1 4

ES6引入的Generator函數(shù),是async/await的基礎(chǔ)。

await讓我們能用同步寫法寫出異步方法,但事實(shí)真的如此嗎?在C#領(lǐng)域,這么說尚且沒錯。后端語言大多支持多線程和線程池,await雖然阻塞了后續(xù)代碼的執(zhí)行,但只是上下文被掛起,線程本身是不會被阻塞的還可以干其它事情,await返回后甚至還可以讓其它線程接手,可參看本人以前的博文async、await在ASP.NET[ MVC]中之線程死鎖的故事。js的話,它是單線程,而且它也不像go一樣有完善的協(xié)程機(jī)制,無法手動(time.sleep()、select{}等)切換代碼塊執(zhí)行——除非等到await返回,否則線程是沒機(jī)會執(zhí)行其它代碼塊的。 錯誤。
注意await掛起的不是線程,而是resolve上下文,推測本質(zhì)上還是與js的執(zhí)行隊列相關(guān),只不過await后續(xù)邏輯都排在resolve之后罷了。

async function test() {
    let p = new Promise((resolve, _) => {        
        setTimeout(() => {
            resolve(1)
        }, 5000)
    });
    setTimeout(() => {
        console.info(2)
    }, 3000)
    let num = await p
    console.info(num)
}

test()

//輸出:2 1

但使用await時仍要注意避免不必要的等待,如果前后幾個Promise沒有依賴關(guān)系(更精確的說法是,任務(wù)的發(fā)起條件不依賴其它任務(wù)的結(jié)果),那么最好同時發(fā)起它們,并在最后await Promise.all(promises)。


異常捕獲

很多文章都說try/catch在異步模式下無效,其實(shí)搭配await的話還是可以的(畢竟await可以使得回調(diào)執(zhí)行在try塊內(nèi)),如下:

let testPromise = function () {
    // throw new Error("異步異常測試")
    return Promise.reject(new Error("異步異常測試"))
}

let testInvocation = async () => {
    try {
        await testPromise()
    } catch (err) {
        console.error(`catch: ${err}`)
    }
}
testInvocation() //輸出:catch: Error: 異步異常測試

如果try的是整個testInvocation()那自然沒戲。

如果覺得在每個異步方法內(nèi)部try/catch太繁瑣,那么可以抽離出一個模板方法,或者使用process對象注冊uncaughtExceptionunhandledRejection事件,注意這兩者的區(qū)別:

process.on('uncaughtException', e => {
    console.error(`uncaughtException: ${e.message}`)
}); 
process.on('unhandledRejection', (reason, promise) => {
    console.error(`unhandledRejection: ${reason}`)
}); 

let testPromise = function(){
    throw new Error("異步異常測試")
}

testPromise() //輸出:uncaughtException: 異步異常測試

let testInvocation = async () => await testPromise() //.catch 因為testPromise()返回的不是Promise,所以catch無效
testInvocation() //輸出:unhandledRejection: Error: 異步異常測試

//注意兩次異常類型不一樣

如果你使用electron開發(fā)桌面應(yīng)用,可能無法[以process.on('unhandledRejection', ...)方式]捕獲unhandledRejection異常(本人使用v10.1.0版本測試發(fā)現(xiàn))。遇到這種情況,只能老老實(shí)實(shí)在每個Promise后面寫catch()。

使用process捕獲異常無法獲取異常的上下文,且丟失上下文堆棧使得node不能正常進(jìn)行內(nèi)存回收,從而導(dǎo)致內(nèi)存泄露。
node中還有個東西domain用于彌補(bǔ)process的問題,但是個人認(rèn)為domain使用不便,且織入業(yè)務(wù)代碼程度過深,另外據(jù)說目前版本還不穩(wěn)定(后續(xù)可能會更改),甚至有文章說已被node廢棄,具體什么情況暫未深入了解。總之希望node或者js平臺能出一個關(guān)于異常捕獲的更好的解決方案。


協(xié)程安全

在js場景下,異步機(jī)制更類似于Go的協(xié)程(畢竟js是單線程,多線程無從談起),所以此處取名為協(xié)程安全。

直接看代碼:

let policy = {}

let testfun = async () => {    
    let data = await policy
    //生成隨機(jī)數(shù)
    data["key"] = utility.getRandomString(20)
    return data
}

//1
let testinfo = async () => {
    let data = await testfun()
    console.info(data.key)    
}

for (let i = 0; i < 5; i++) {
    testinfo()
}

//輸出結(jié)果是5次相同的隨機(jī)數(shù)

//2
let testinfo2 = async () => {
    for (let i = 0; i < 5; i++) {
        let data = await testfun()
        console.info(data.key)
    }
}

testinfo2()

//如此則正常輸出5次不同的隨機(jī)數(shù)

由上可知:在使用await時,若多個await操作相同變量,并且它們的后續(xù)操作是在所有await都返回后執(zhí)行,就容易出現(xiàn)與預(yù)期不符的情況,應(yīng)盡量避免。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多