欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

圖解Promise實(shí)現(xiàn)原理(一)——基礎(chǔ)實(shí)現(xiàn)

本文首發(fā)于 vivo互聯(lián)網(wǎng)技術(shù) 微信公眾號(hào) 
鏈接: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ
作者:孔垂亮

目前創(chuàng)新互聯(lián)建站已為成百上千家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬主機(jī)綿陽(yáng)服務(wù)器托管、企業(yè)網(wǎng)站設(shè)計(jì)、同心網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶(hù)導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶(hù)和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

很多同學(xué)在學(xué)習(xí) Promise 時(shí),知其然卻不知其所以然,對(duì)其中的用法理解不了。 本系列文章由淺入深逐步實(shí)現(xiàn) Promise,并結(jié)合流程圖、實(shí)例以及動(dòng)畫(huà)進(jìn)行演示,達(dá)到深刻理解 Promise 用法的目的。

本系列文章有如下幾個(gè)章節(jié)組成:

  1. 圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

  2. 圖解 Promise 實(shí)現(xiàn)原理(二)—— Promise 鏈?zhǔn)秸{(diào)用

  3. 圖解 Promise 實(shí)現(xiàn)原理(三)—— Promise 原型方法實(shí)現(xiàn)

  4. 圖解 Promise 實(shí)現(xiàn)原理(四)—— Promise 靜態(tài)方法實(shí)現(xiàn)

本文適合對(duì) Promise 的用法有所了解的人閱讀,如果還不清楚,請(qǐng)自行查閱阮一峰老師的 《 ES6入門(mén) 之 Promise 對(duì)象》。

Promise 規(guī)范有很多,如 Promise/A,Promise/B,Promise/D 以及 Promise/A 的升級(jí)版 Promise/A+,有興趣的可以去了解下,最終 ES6 中采用了 Promise/A+ 規(guī)范。所以本文的Promise源碼是按照 Promise/A+規(guī)范來(lái)編寫(xiě)的(不想看英文版的移步 Promise/A+規(guī)范中文翻譯)。

引子

為了讓大家更容易理解,我們從一個(gè)場(chǎng)景開(kāi)始,一步一步跟著思路思考,會(huì)更容易看懂。

考慮下面一種獲取用戶(hù) id 的請(qǐng)求處理:

//不使用Promise        http.get('some_url', function (result) {
    //do something
    console.log(result.id);
});//使用Promisenew Promise(function (resolve) {
    //異步請(qǐng)求
    http.get('some_url', function (result) {
        resolve(result.id)
    })
}).then(function (id) {
    //do something
    console.log(id);
})

圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

乍一看,好像不使用 Promise 更簡(jiǎn)潔一些。其實(shí)不然,設(shè)想一下,如果有好幾個(gè)依賴(lài)的前置請(qǐng)求都是異步的,此時(shí)如果沒(méi)有 Promise ,那回調(diào)函數(shù)要一層一層嵌套,看起來(lái)就很不舒服了。如下:

//不使用Promise        http.get('some_url', function (id) {
    //do something
    http.get('getNameById', id, function (name) {
        //do something
        http.get('getCourseByName', name, function (course) {
            //dong something
            http.get('getCourseDetailByCourse', function (courseDetail) {
                //do something
            })
        })
    })
});//使用Promisefunction getUserId(url) {
    return new Promise(function (resolve) {
        //異步請(qǐng)求
        http.get(url, function (id) {
            resolve(id)
        })
    })
}
getUserId('some_url').then(function (id) {
    //do something
    return getNameById(id); // getNameById 是和 getUserId 一樣的Promise封裝。下同}).then(function (name) {
    //do something
    return getCourseByName(name);
}).then(function (course) {
    //do something
    return getCourseDetailByCourse(course);
}).then(function (courseDetail) {
    //do something});
圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)實(shí)現(xiàn)原理

說(shuō)到底,Promise 也還是使用回調(diào)函數(shù),只不過(guò)是把回調(diào)封裝在了內(nèi)部,使用上一直通過(guò) then 方法的鏈?zhǔn)秸{(diào)用,使得多層的回調(diào)嵌套看起來(lái)變成了同一層的,書(shū)寫(xiě)上以及理解上會(huì)更直觀和簡(jiǎn)潔一些。

一、基礎(chǔ)版本

//極簡(jiǎn)的實(shí)現(xiàn)class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        this.callbacks.push(onFulfilled);
    }
    _resolve(value) {        this.callbacks.forEach(fn => fn(value));
    }
}//Promise應(yīng)用let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('5秒');
    }, 5000);
}).then((tip) => {
    console.log(tip);
})
圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)上述代碼很簡(jiǎn)單,大致的邏輯是這樣的:
  1. 調(diào)用 then 方法,將想要在 Promise 異步操作成功時(shí)執(zhí)行的 onFulfilled 放入callbacks隊(duì)列,其實(shí)也就是注冊(cè)回調(diào)函數(shù),可以向觀察者模式方向思考;

  2. 創(chuàng)建 Promise 實(shí)例時(shí)傳入的函數(shù)會(huì)被賦予一個(gè)函數(shù)類(lèi)型的參數(shù),即 resolve,它接收一個(gè)參數(shù) value,代表異步操作返回的結(jié)果,當(dāng)異步操作執(zhí)行成功后,會(huì)調(diào)用resolve方法,這時(shí)候其實(shí)真正執(zhí)行的操作是將 callbacks 隊(duì)列中的回調(diào)一一執(zhí)行。

圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

(圖:基礎(chǔ)版本實(shí)現(xiàn)原理)

首先 new Promise 時(shí),傳給 Promise 的函數(shù)設(shè)置定時(shí)器模擬異步的場(chǎng)景,接著調(diào)用 Promise 對(duì)象的 then 方法注冊(cè)異步操作完成后的 onFulfilled,最后當(dāng)異步操作完成時(shí),調(diào)用 resolve(value), 執(zhí)行 then 方法注冊(cè)的 onFulfilled。

then 方法注冊(cè)的 onFulfilled 是存在一個(gè)數(shù)組中,可見(jiàn) then 方法可以調(diào)用多次,注冊(cè)的多個(gè)onFulfilled 會(huì)在異步操作完成后根據(jù)添加的順序依次執(zhí)行。如下:

//then 的說(shuō)明let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('5秒');
    }, 5000);
});
p.then(tip => {
    console.log('then1', tip);
});
p.then(tip => {
    console.log('then2', tip);
});

圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)上例中,要先定義一個(gè)變量 p ,然后 p.then 兩次。而規(guī)范中要求,then 方法應(yīng)該能夠鏈?zhǔn)秸{(diào)用。 實(shí)現(xiàn)也簡(jiǎn)單,只需要在 then 中 return this 即可。如下所示:

//極簡(jiǎn)的實(shí)現(xiàn)+鏈?zhǔn)秸{(diào)用class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        this.callbacks.push(onFulfilled);        return this;//看這里
    }
    _resolve(value) {        this.callbacks.forEach(fn => fn(value));
    }
}let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('5秒');
    }, 5000);
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});
圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

(圖:基礎(chǔ)版本的鏈?zhǔn)秸{(diào)用)

二、加入延遲機(jī)制

上面 Promise 的實(shí)現(xiàn)存在一個(gè)問(wèn)題:如果在 then 方法注冊(cè) onFulfilled 之前,resolve 就執(zhí)行了,onFulfilled 就不會(huì)執(zhí)行到了。比如上面的例子中我們把 setTimout 去掉:

//同步執(zhí)行了resolvelet p = new Promise(resolve => {
    console.log('同步執(zhí)行');
    resolve('同步執(zhí)行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

執(zhí)行結(jié)果顯示,只有 "同步執(zhí)行" 被打印了出來(lái),后面的 "then1" 和 "then2" 均沒(méi)有打印出來(lái)。再回去看下 Promise 的源碼,也很好理解,resolve 執(zhí)行時(shí),callbacks 還是空數(shù)組,還沒(méi)有onFulfilled 注冊(cè)上來(lái)。

這顯然是不允許的,Promises/A+規(guī)范明確要求回調(diào)需要通過(guò)異步方式執(zhí)行,用以保證一致可靠的執(zhí)行順序。因此要加入一些處理,保證在 resolve 執(zhí)行之前,then 方法已經(jīng)注冊(cè)完所有的回調(diào):

//極簡(jiǎn)的實(shí)現(xiàn)+鏈?zhǔn)秸{(diào)用+延遲機(jī)制class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        this.callbacks.push(onFulfilled);        return this;
    }
    _resolve(value) {
        setTimeout(() => {//看這里
            this.callbacks.forEach(fn => fn(value));
        });
    }
}
圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)在 resolve 中增加定時(shí)器,通過(guò) setTimeout 機(jī)制,將 resolve 中執(zhí)行回調(diào)的邏輯放置到JS任務(wù)隊(duì)列末尾,以保證在 resolve 執(zhí)行時(shí),then方法的 onFulfilled 已經(jīng)注冊(cè)完成。

圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

(圖:延遲機(jī)制)

但是這樣依然存在問(wèn)題,在 resolve 執(zhí)行后,再通過(guò) then 注冊(cè)上來(lái)的 onFulfilled 都沒(méi)有機(jī)會(huì)執(zhí)行了。如下所示,我們加了延遲后,then1 和 then2 可以打印出來(lái)了,但下例中的 then3 依然打印不出來(lái)。所以我們需要增加狀態(tài),并且保存 resolve 的值。

let p = new Promise(resolve => {
    console.log('同步執(zhí)行');
    resolve('同步執(zhí)行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});
setTimeout(() => {
    p.then(tip => {
        console.log('then3', tip);
    })
});

三、增加狀態(tài)

為了解決上一節(jié)拋出的問(wèn)題,我們必須加入狀態(tài)機(jī)制,也就是大家熟知的 pending、fulfilled、rejected。

Promises/A+ 規(guī)范中明確規(guī)定了,pending 可以轉(zhuǎn)化為 fulfilled 或 rejected 并且只能轉(zhuǎn)化一次,也就是說(shuō)如果 pending 轉(zhuǎn)化到 fulfilled 狀態(tài),那么就不能再轉(zhuǎn)化到 rejected。并且 fulfilled 和 rejected 狀態(tài)只能由 pending 轉(zhuǎn)化而來(lái),兩者之間不能互相轉(zhuǎn)換。

圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

增加狀態(tài)后的實(shí)現(xiàn)是這樣的

//極簡(jiǎn)的實(shí)現(xiàn)+鏈?zhǔn)秸{(diào)用+延遲機(jī)制+狀態(tài)class Promise {
    callbacks = [];
    state = 'pending';//增加狀態(tài)
    value = null;//保存結(jié)果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {        if (this.state === 'pending') {//在resolve之前,跟之前邏輯一樣,添加到callbacks中
            this.callbacks.push(onFulfilled);
        } else {//在resolve之后,直接執(zhí)行回調(diào),返回結(jié)果了
            onFulfilled(this.value);
        }        return this;
    }
    _resolve(value) {        this.state = 'fulfilled';//改變狀態(tài)
        this.value = value;//保存結(jié)果
        this.callbacks.forEach(fn => fn(value));
    }
}

注意:當(dāng)增加完?duì)顟B(tài)之后,原先的_resolve中的定時(shí)器可以去掉了。當(dāng)reolve同步執(zhí)行時(shí),雖然callbacks為空,回調(diào)函數(shù)還沒(méi)有注冊(cè)上來(lái),但沒(méi)有關(guān)系,因?yàn)楹竺孀?cè)上來(lái)時(shí),判斷狀態(tài)為fulfilled,會(huì)立即執(zhí)行回調(diào)。

圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)圖解 Promise 實(shí)現(xiàn)原理(一)—— 基礎(chǔ)實(shí)現(xiàn)

(圖:Promise 狀態(tài)管理)

實(shí)現(xiàn)源碼中只增加了 fulfilled 的狀態(tài) 和 onFulfilled 的回調(diào),但為了完整性,在示意圖中增加了 rejected 和 onRejected 。后面章節(jié)會(huì)實(shí)現(xiàn)。

resolve 執(zhí)行時(shí),會(huì)將狀態(tài)設(shè)置為 fulfilled ,并把 value 的值存起來(lái),在此之后調(diào)用 then 添加的新回調(diào),都會(huì)立即執(zhí)行,直接返回保存的value值。

(Promise 狀態(tài)變化演示動(dòng)畫(huà))

詳情請(qǐng)點(diǎn)擊: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ

至此,一個(gè)初具功能的Promise就實(shí)現(xiàn)好了,它實(shí)現(xiàn)了 then,實(shí)現(xiàn)了鏈?zhǔn)秸{(diào)用,實(shí)現(xiàn)了狀態(tài)管理等等。但仔細(xì)想想,鏈?zhǔn)秸{(diào)用的實(shí)現(xiàn)只是在 then 中 return 了 this,因?yàn)槭峭粋€(gè)實(shí)例,調(diào)用再多次 then 也只能返回相同的一個(gè)結(jié)果,這顯然是不能滿(mǎn)足我們的要求的。下一節(jié),講述如何實(shí)現(xiàn)真正的鏈?zhǔn)秸{(diào)用。

當(dāng)前題目:圖解Promise實(shí)現(xiàn)原理(一)——基礎(chǔ)實(shí)現(xiàn)
網(wǎng)站地址:http://chinadenli.net/article0/pigeoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊(cè)網(wǎng)站設(shè)計(jì)公司建站公司定制網(wǎng)站用戶(hù)體驗(yàn)動(dòng)態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)