JaveScript中的Promise對象


前言

本文旨在簡單講解一下javascript中的Promise對象的概念,特性與簡單的使用方法造挽。并在文末會(huì)附上一份符合PromiseA+規(guī)范的Promise對象的完整實(shí)現(xiàn)碱璃。

注:本文中的相關(guān)概念均基于PromiseA+規(guī)范。

相關(guān)參考

JavaScript Promise迷你書

Promise/A+規(guī)范



正文

1.Promise簡介

在了解javescript中的Promise實(shí)現(xiàn)之前有必要先了解一下Promise的概念饭入。

什么是Promise嵌器?

關(guān)于Promise概念的解釋,網(wǎng)上的各種資料眾說紛紜谐丢,這里奉上筆者自己的理解爽航。簡單來說,Promise就是一套處理異步事件的方式和流程乾忱。promise在英文中的含義是約定讥珍,而針對異步事件特性的處理方式與這個(gè)含義非常吻合。

為什么要使用Promise窄瘟?

一個(gè)異步事件不會(huì)立刻返回結(jié)果串述,這時(shí)我們就需要預(yù)先規(guī)定一些操作,等待異步事件返回結(jié)果后寞肖,再去使用某種方式讓預(yù)先規(guī)定的操作執(zhí)行纲酗。在javascript的習(xí)慣中,我們常用回調(diào)函數(shù)(callback)去實(shí)現(xiàn)上述過程新蟆。下面是一個(gè)簡單的示例:

例1

let asyncFunc = function(callback){

? ? let num = 100;

? ? setTimeout(function(){

? ? ? ? num += 100;

? ? ? ? callback(num);

? ? },2000);

};

function foo(value){

? ? console.log(value);? //value => 200

}

asyncFunc (foo);

上面就是一個(gè)簡單的異步操作處理過程觅赊,asyncFunc就是一個(gè)異步的函數(shù),執(zhí)行后通過setTimeout方法在2秒返回了一個(gè)值琼稻,而foo則是一個(gè)回調(diào)函數(shù)吮螺,通過傳入異步函數(shù)并且在返回結(jié)果后被調(diào)用的方式獲取異步操作的結(jié)果。這里的回調(diào)函數(shù)就如同一個(gè)事先的約定帕翻,在異步操作返回結(jié)果后立即被實(shí)現(xiàn)鸠补。

那么,既然js中已經(jīng)有處理異步事件的方法嘀掸,為何還要引入Promise這個(gè)新的方式呢紫岩?實(shí)際上,上面這段代碼只是簡單展示下回調(diào)函數(shù)的基礎(chǔ)使用睬塌,而在真正的使用場景中泉蝌,我們不得不面對各種十分復(fù)雜的局面。通常在一個(gè)異步操作返回結(jié)果后執(zhí)行的回調(diào)中還要進(jìn)行另一個(gè)異步操作揩晴,而同一個(gè)異步操作返回結(jié)果后要執(zhí)行的回調(diào)函數(shù)可不止一個(gè)勋陪。數(shù)個(gè)異步操作與回調(diào)函數(shù)彼此嵌套,時(shí)刻挑戰(zhàn)者維護(hù)和使用者的神經(jīng)硫兰。下面是一個(gè)彼此嵌套的例子:

例2

ajax(url1,function(value1){

? ? foo(value1);

? ? bar();

});

function foo(value){

? ? ajax(url2,function(value2){

? ? ? ? do something..

? ? ? ? ajax(url3,function(value3){

? ? ? ? ? ? ...

? ? ? ? })

? ? });

}

function bar(){ do something.. };

上面的例子模擬了一個(gè)js中一個(gè)常用的異步操作:發(fā)送ajax請求數(shù)據(jù)诅愚。在url1請求的回調(diào)中使用了foo和bar兩個(gè)函數(shù),而foo中又發(fā)送了url2劫映,url3的請求违孝。刹前。。這樣數(shù)層嵌套下來等浊,最終導(dǎo)致代碼非常的不直觀腮郊,維護(hù)起來難度也直線上升,形成常說的“回調(diào)地獄”筹燕。

了解了傳統(tǒng)上js處理異步操作的復(fù)雜和困難后轧飞,我們不禁思索,是否有方法能夠更加簡潔撒踪,直觀的去解決異步操作的種種問題过咬?答案就是我們這篇文章的主角:Promise。

2. Promise的特性及使用

在PromiseA+規(guī)范中做出了這樣定義:promise是一個(gè)包含了兼容Promise規(guī)范then方法的對象或函數(shù)制妄,與Promise最主要的交互方法是通過將函數(shù)傳入它的then方法從而獲取得Promise最終的值或Promise最終最拒絕(reject)的原因掸绞。

? 這段定義有兩個(gè)重點(diǎn):1.Promise是一個(gè)對象或函數(shù)? 2.它有一個(gè)then方法,能夠獲取prmose的最終結(jié)果耕捞。下面我們就來實(shí)際看一下Promise到底是如何處理異步事件的衔掸,我們將上面的例1使用Promise進(jìn)行一下改寫:

例3

let p = new Promise(function(resolve,reject){

? ? let value = 100;

? ? setTimeout(function(){

? ? ? ? value += 100;

? ? ? ? resolve(value);

? ? },2000);

});

p.then(function(value){

? ? console.log(value);? ? ? //value => 200

},function(err){

? ? do something...

});

初看之下其實(shí)并沒有太大區(qū)別,但實(shí)際上Promise的威力在更復(fù)雜的場景下才能更好的發(fā)揮俺抽。我們先針對這個(gè)簡單的例子來講解下Promise的使用

首先通過 new 關(guān)鍵字實(shí)例化一個(gè)Promise對象敞映,在這個(gè)對象中傳入一個(gè)要執(zhí)行異步操作的函數(shù)。這個(gè)函數(shù)包含兩個(gè)形參:resolve和reject磷斧。這兩個(gè)形參是Promise中定義的2個(gè)函數(shù)振愿,分別在異步事件成功和失敗時(shí)調(diào)用。例3中我們在2秒后調(diào)用了resolve函數(shù)弛饭,代表著異步事件成功冕末,返回一個(gè)值。而在我們實(shí)例化Promise對象的同時(shí)侣颂,我們又調(diào)用了這個(gè)實(shí)例的then方法档桃。then方法可以說是Promise方法中的核心,它即代表著Promise約定的這層含義横蜒,在then方法中接收2個(gè)函數(shù)作為參數(shù)胳蛮,分別在異步事件成功時(shí)或失敗時(shí)執(zhí)行,并且兩個(gè)函數(shù)的參數(shù)正是異步事件成功時(shí)返回的值或失敗時(shí)原因。

其實(shí)丛晌,使用Promise對象來處理異步事件比起使用傳統(tǒng)的回調(diào)函數(shù)的一個(gè)優(yōu)點(diǎn)在于:Promise規(guī)范了處理異步事件的流程。我們不必再深入異步事件的內(nèi)部斗幼,去分析種種狀態(tài)變化后對應(yīng)的回調(diào)究竟如何調(diào)用澎蛛,也不必過多考慮異步事件內(nèi)部發(fā)生錯(cuò)誤時(shí)該如何捕獲,我們只需要在合適的時(shí)候通知Promise返回成功或失敗狀態(tài)蜕窿,剩下的事統(tǒng)統(tǒng)交給Promise去解決谋逻。

以上我們大致了解了Promise的處理流程,在詳細(xì)講解Promise對象中的方法之前有必要先了解一下Promise的狀態(tài)概念毁兆。

一個(gè)Promise對象在實(shí)例化后可能擁有以下3種狀態(tài)的其中之一:

Fulfilled - 當(dāng)傳入的異步事件成功返回值時(shí)的狀態(tài)

Rejected - 當(dāng)傳入的異步事件失敗或產(chǎn)生異常時(shí)的狀態(tài)

Pending -? 當(dāng)傳入的異步事件還沒有結(jié)果返回時(shí)的狀態(tài)

注意,任何時(shí)候Promise對象都只能處于以上其中狀態(tài)的一種梅桩,當(dāng)Promise對象處于Pending狀態(tài)時(shí)壹粟,它可以轉(zhuǎn)化成Fulfilled 或Rejected 狀態(tài),而當(dāng)Promise對象處于Fulfilled 或Rejected狀態(tài)時(shí)宿百,它不能再轉(zhuǎn)化成其他狀態(tài)趁仙。

可以用一張圖來直白的表示上面這段話


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(圖片取自Promise迷你書)

在了解了Promise的三種狀態(tài)后 ,接下來可以詳細(xì)了解下Promise對象的幾個(gè)方法

resolve()

resolve方法是在一個(gè)Promise對象實(shí)例化時(shí)傳入的任務(wù)函數(shù)的第一個(gè)參數(shù)垦页,它的作用是讓Promise進(jìn)入“Fulfilled ”狀態(tài)雀费,resolve方法只接受一個(gè)參數(shù),即異步事件的返回值value外臂。

reject()

reject方法與resolve方法正好相反坐儿,它是在一個(gè)Promise對象實(shí)例化時(shí)傳入的任務(wù)函數(shù)的第二個(gè)參數(shù),它的作用是讓Promise進(jìn)入“Rejected”狀態(tài)宋光,reject方法同樣只接受一個(gè)參數(shù)貌矿,即異步事件失敗或異常的原因reason。

Promise.prototype.then()

then方法是Promise對象方法的重中之重罪佳,它是Promise實(shí)例的方法逛漫,用來注冊Promise對象成功時(shí)執(zhí)行的回調(diào)函數(shù)(onFulfilled)和失敗時(shí)執(zhí)行的回調(diào)函數(shù)(onRejected)。一個(gè)then方法的返回值仍然是一個(gè)Promsie對象赘艳。因此酌毡,then方法支持鏈?zhǔn)秸{(diào)用,也就是一個(gè)一個(gè)then方法的返回值可以繼續(xù)調(diào)用then蕾管。而相鏈接的then方法中枷踏,在上一個(gè)then方法的onFulfilled或onRejected回調(diào)函數(shù)中通過 return value(reason)的方式,把這個(gè)結(jié)果作為下一個(gè)then中的回調(diào)函數(shù)的參數(shù)被接收掰曾。onFulfilled和onRejected函數(shù)的返回值可以是任何javascript值旭蠕,甚至一個(gè)Promise對象的成功或失敗時(shí)的回調(diào)函數(shù)可以返回一個(gè)新的Promise對象。這樣的特性使得例2中那種復(fù)雜的異步事件嵌套的場景處理得以簡化。下面是使用Promise來重寫的例2:

例4

let p1 = new Promise(function(resolve,reject){

? ? ajax(url1,function(value1){

? ? ? ? resolve(value1);

? ? });

});

p1.then(function(value1){

? ? return new Promise(function(resolve,reject){

? ? ? ? ajax(url2,function(value2){

? ? ? ? ? ? do something..

? ? ? ? ? ? resolve(value2);

? ? ? ? });

? ? })

}).then(function(value2){

? ? return new Promise(function(resolve,reject){

? ? ? ? ajax(url3,function(value3){

? ? ? ? ? ? ...

? ? ? ? });

? ? })

});

p1.then(bar);

function bar(){do something...};

可以看出掏熬,使用Promise改寫后的代碼結(jié)構(gòu)上更加清晰佑稠,它把層層嵌套的函數(shù)轉(zhuǎn)化成鏈?zhǔn)降恼{(diào)用then方法的形式,這樣可以非常清晰的看出事件間的關(guān)系和執(zhí)行順序旗芬,大大降低了日后代碼使用和維護(hù)的難度舌胶。

關(guān)于then方法還有幾點(diǎn)補(bǔ)充:

1. then方法中的onFulfilled和onRejected方法都是可以省略的。

2. 當(dāng)一個(gè)Promise失敗返回了reason疮丛,而then方法中沒有定義onRejected函數(shù)時(shí)幔嫂,這個(gè)reason會(huì)被鏈?zhǔn)秸{(diào)用的下一個(gè)then方法的onRejected方法接收。

3. 一個(gè)Promise實(shí)例可以調(diào)用多次then方法这刷,這些then注冊的onFulfilled和onRejected函數(shù)會(huì)按照注冊的順序執(zhí)行婉烟。

Promise.prototype.catch()

catch方法是一個(gè)then方法的語法糖,它只接受一個(gè)失敗處理函數(shù)onRejected暇屋,實(shí)際上等同于以下代碼:

new Promsie.then(null,function(){

? ? do something...

})

Promise.all()

all方法是Promsie對象的靜態(tài)方法似袁,使用方式是 Promise.all()。all方法接收的參數(shù)為一個(gè)包含數(shù)個(gè)Promise對象實(shí)例的數(shù)組咐刨,并返回一個(gè)新的Promise實(shí)例昙衅。當(dāng)數(shù)組中所有的Promse實(shí)例都返回結(jié)果后,將所有數(shù)組中的Promise實(shí)例的成功返回值傳入一個(gè)數(shù)組定鸟,并將這個(gè)數(shù)組注入到all方法返回的新實(shí)例的then方法中而涉。下面是一個(gè)all方法的使用實(shí)例:

例5

let promiseArr = [

? ? new Promise(function(resolve,reject){

? ? ? ? setTimeout(function(){

? ? ? ? ? ? resolve(100)

? ? ? ? },1000)

? ? }),

? ? new Promise(function(resolve,reject){

? ? ? ? setTimeout(function(){

? ? ? ? ? ? resolve(200)

? ? ? ? },500)

? ? })

]

Promise.all(promiseArr).then(function(valArr){

? ? console.log(valArr)? ? // valArr? => [100,200]

},function(err){

? ? do something...

})

all方法值得注意的有兩點(diǎn):

1.數(shù)組中所有promise實(shí)例都成功后的返回值,在valArr中的順序是按照promiseArr 中promise實(shí)例的順序來排列的联予。

2.當(dāng)任何一個(gè)promise失敗后啼县,all方法直接將返回的Promise對象的狀態(tài)變?yōu)镽ejected,并調(diào)用then方法的onRejected函數(shù)沸久,把失敗的原因傳遞出來季眷。

Promise.resolve()

Promsie對象本身存在一個(gè)resolve方法,它的作用是立刻返回一個(gè)狀態(tài)為Fulfilled的Promise對象實(shí)例卷胯。如果你在這個(gè)resolve方法中傳入的是一個(gè)Promise實(shí)例的話子刮,那么resolve方法會(huì)保持這個(gè)Promise實(shí)例的狀態(tài),并根據(jù)它最后返回的狀態(tài)來調(diào)用resolve方法返回的Promise實(shí)例then方法的onResolve或onRejected函數(shù)窑睁。

其實(shí)這個(gè)方法最常用的場景是講一個(gè)普通的值轉(zhuǎn)換成一個(gè)Promise實(shí)例挺峡。一般來說不是很常用。

Promise.reject()

與Promise.resolve()相反担钮,它的作用是立刻返回一個(gè)狀態(tài)為Rejected的Promise對象實(shí)例橱赠。實(shí)際上這個(gè)方法是一個(gè)語法糖,它等同于以下代碼:

new Promise(function(resolve,reject){

? ? reject(reason);

})

以上就是一個(gè)ES6中的Promise對象中所包含的常用方法箫津。

3. 一個(gè)符合PromiseA+規(guī)范的Promise對象的完整實(shí)現(xiàn)

? 想必看到這里的一些讀者會(huì)不禁思考病线,Promise對象究竟是如何實(shí)現(xiàn)的呢吓著?我個(gè)人參考了一些資料實(shí)現(xiàn)了一個(gè)符合PromiseA+規(guī)范的Promise對象鲤嫡,把源代碼貼在下面送挑,有興趣的朋友可以參考一下,實(shí)際上代碼本身并不是很多暖眼,各位看完以后可以嘗試用自己的方式再實(shí)現(xiàn)一遍惕耕。同時(shí)附上一個(gè)測試工具,里面包含了幾百個(gè)測試用例诫肠,用來測試我們自己寫的Promise是否完美的符合PromiseA+規(guī)范司澎。

Compliances tests for Promises/A+

使用的方法很簡單

npm i -g promises-aplus-tests

promises-aplus-tests Promise.js

安裝后運(yùn)行你的js文件就可以測試你的代碼是否符合規(guī)范了。

下面就是我實(shí)現(xiàn)的Promise對象的代碼


function MyPromise(task) {

? ? const _this = this;

? ? _this.status = 'pending';? //設(shè)定初始狀態(tài)

? ? _this.value = undefined;

? ? _this.onFulfilledsList = [];? //onFulfilled函數(shù)序列

? ? _this.onRejectedsList = [];? //onRejected函數(shù)序列

? ? function resolve(value) {

? ? ? ? if (value instanceof MyPromise) {

? ? ? ? ? ? return value.then(resolve, reject);

? ? ? ? }

? ? ? ? //異步執(zhí)行resolve或reject方法栋豫,保證代碼的統(tǒng)一性和注冊的回調(diào)函數(shù)按照正確的順序執(zhí)行


? ? ? ? ? ? if (_this.status === 'pending') {

? ? ? ? ? ? ? ? _this.status = 'fulfilled';

? ? ? ? ? ? ? ? _this.value = value;

? ? ? ? ? ? ? ? _this.onFulfilledsList.forEach(cb => cb(value))

? ? ? ? ? ? }


? ? }

? ? function reject(reason) {


? ? ? ? ? ? if (_this.status === 'pending') {

? ? ? ? ? ? ? ? _this.status = 'rejected';

? ? ? ? ? ? ? ? _this.reason = reason;

? ? ? ? ? ? ? ? _this.onRejectedsList.forEach(cb => cb(reason))

? ? ? ? ? ? }


? ? }

? ? try {

? ? ? ? task(resolve, reject);

? ? } catch (err) {

? ? ? ? throw new Error(err);

? ? }

}

function resolvePromise(promise2, x, resolve, reject) {

? ? if (x === promise2) {

? ? ? ? return reject(new TypeError('循環(huán)引用'));

? ? }

? ? //如果返回的是一個(gè)thenable對象挤安,即一個(gè)擁有then方法的對象,那么使用它的then方法去獲得它的最終返回值丧鸯。目的是為了兼容其他Promise庫

? ? if (x !== null && (typeof x === 'object' || typeof x === 'function')) {

? ? ? ? let then, called;

? ? ? ? try {

? ? ? ? ? ? then = x.then;

? ? ? ? ? ? if (typeof then === 'function') {

? ? ? ? ? ? ? ? then.call(x, function (newx) {

? ? ? ? ? ? ? ? ? ? if (called) return;? //防止重復(fù)調(diào)用

? ? ? ? ? ? ? ? ? ? called = true;

? ? ? ? ? ? ? ? ? ? resolvePromise(promise2, newx, resolve, reject);

? ? ? ? ? ? ? ? }, function (err) {

? ? ? ? ? ? ? ? ? ? if (called) return;

? ? ? ? ? ? ? ? ? ? called = true;

? ? ? ? ? ? ? ? ? ? return reject(err);

? ? ? ? ? ? ? ? });

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? resolve(x);

? ? ? ? ? ? }

? ? ? ? } catch (err) {

? ? ? ? ? ? if (called) return;

? ? ? ? ? ? called = true;

? ? ? ? ? ? reject(err);

? ? ? ? }

? ? } else {

? ? ? ? resolve(x);

? ? }

}

MyPromise.prototype.then = function (onFulfilled, onRejected) {

? ? const _this = this;

? ? let promise2;

? ? onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (data) {

? ? ? ? return data;

? ? };

? ? onRejected = typeof onRejected === 'function' ? onRejected : function (data) {

? ? ? ? throw data;

? ? };

? ? //為了支持同步代碼蛤铜,當(dāng)then方法注冊的時(shí)候如果Promise的狀態(tài)已經(jīng)改變,那么立即執(zhí)行對應(yīng)的函數(shù)

? ? if (_this.status === 'fulfilled') {

? ? ? ? promise2 = new MyPromise(function (resolve, reject) {

? ? ? ? ? setTimeout(function () {

? ? ? ? ? ? let x;

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? x = onFulfilled(_this.value);

? ? ? ? ? ? ? ? resolvePromise(promise2, x, resolve, reject);

? ? ? ? ? ? } catch (err) {

? ? ? ? ? ? ? ? reject(err);

? ? ? ? ? ? }

? ? ? ? ? })

? ? ? ? })

? ? }

? ? if (_this.status === 'rejected') {

? ? ? ? promise2 = new MyPromise(function (resolve, reject) {

? ? ? ? ?setTimeout(function () {

? ? ? ? ? ? let x;

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? x = onRejected(_this.reason);

? ? ? ? ? ? ? ? resolvePromise(promise2, x, resolve, reject);

? ? ? ? ? ? } catch (err) {

? ? ? ? ? ? ? ? reject(err);

? ? ? ? ? ? }

? ? ? ? ?)}

? ? ? ? })

? ? }

? ? if (_this.status === 'pending') {

? ? ? ? promise2 = new MyPromise(function (resolve, reject) {

? ? ? ? ? ? _this.onFulfilledsList.push(function (value) {

? ? ? ? ????????setTimeout(function () {

? ? ? ? ? ? ? ? let x;

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? x = onFulfilled(value);

? ? ? ? ? ? ? ? ? ? resolvePromise(promise2, x, resolve, reject);

? ? ? ? ? ? ? ? } catch (err) {

? ? ? ? ? ? ? ? ? ? reject(err);

? ? ? ? ? ? ? ? }

????????????????})

? ? ? ? ? ? });

? ? ? ? ? ? _this.onRejectedsList.push(function (reason) {

? ? ? ? ? ? ? ?setTimeout(function () {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? let x = onRejected(reason);

? ? ? ? ? ? ? ? ? ? resolvePromise(promise2, x, resolve, reject);

? ? ? ? ? ? ? ? } catch (err) {

? ? ? ? ? ? ? ? ? ? reject(err);

? ? ? ? ? ? ? ? }

????????????})

????????});

? ? ? ? })

? ? }

? ? return promise2;? //返回一個(gè)新的Promise實(shí)例丛肢,以便支持鏈?zhǔn)秸{(diào)用

};

MyPromise.prototype.catch = function (onRejected) {

? ? this.then(null, onRejected);

};

MyPromise.all = function (someValue) {

? ? let resolveValArr = [];

? ? let count = promiseLen = 0;

? ? let promise2;

? ? promise2 = new MyPromise(function (resolve, reject) {

? ? ? ? let iNow = 0;

? ? ? ? try {

? ? ? ? ? ? for (let item of someValue) {

? ? ? ? ? ? ? ? if (item !== null && typeof item === "object") {

? ? ? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? ? ? let then = item.then;

? ? ? ? ? ? ? ? ? ? ? ? let index = iNow;

? ? ? ? ? ? ? ? ? ? ? ? if (typeof then === 'function') {

? ? ? ? ? ? ? ? ? ? ? ? ? ? promiseLen++;

? ? ? ? ? ? ? ? ? ? ? ? ? ? then.call(item, function (value) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? resolveValArr[index] = value;

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (++count === promiseLen) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? resolve(resolveValArr)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? ? ? }, function (err) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? reject(err);

? ? ? ? ? ? ? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? } catch (err) {

? ? ? ? ? ? ? ? ? ? ? ? resolveValArr[iNow] = item;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? resolveValArr[iNow] = item;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? iNow++;

? ? ? ? ? ? }

? ? ? ? ? ? if (iNow === 0) {

? ? ? ? ? ? ? ? return resolve(someValue);

? ? ? ? ? ? }

? ? ? ? ? ? if (promiseLen === 0) {

? ? ? ? ? ? ? ? return resolve(resolveValArr);

? ? ? ? ? ? }

? ? ? ? } catch (err) {

? ? ? ? ? ? reject(new TypeError('無法遍歷的類型!'));

? ? ? ? }

? ? });

? ? return promise2;

};

MyPromise.race = function (someValue) {

? ? let promise2;

? ? promise2 = new MyPromise(function (resolve, reject) {

? ? ? ? let iNow = 0;

? ? ? ? try {

? ? ? ? ? ? for (let item of someValue) {

? ? ? ? ? ? ? ? if (item !== null && typeof item === "object") {

? ? ? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? ? ? let then = item.then;

? ? ? ? ? ? ? ? ? ? ? ? then.call(item, function (value) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? resolve(value);

? ? ? ? ? ? ? ? ? ? ? ? }, function (err) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? reject(err);

? ? ? ? ? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? ? ? } catch (err) {

? ? ? ? ? ? ? ? ? ? ? ? resolve(item);

? ? ? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? resolve(item);

? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? iNow++;

? ? ? ? ? ? }

? ? ? ? ? ? if (iNow === 0) {

? ? ? ? ? ? ? ? return resolve(someValue);

? ? ? ? ? ? }

? ? ? ? } catch (err) {

? ? ? ? ? ? reject(new TypeError('無法遍歷的類型!'));

? ? ? ? }

? ? });

? ? return promise2;

};

MyPromise.resolve = function (value) {

? ? let promise2;

? ? if (value !== null && (typeof value === 'object' || typeof value === 'function')) {

? ? ? ? promise2 = new MyPromise(function (resolve, reject) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? let then = value.then;

? ? ? ? ? ? ? ? if (typeof value.then === 'function') {

? ? ? ? ? ? ? ? ? ? then.call(value, function (data) {

? ? ? ? ? ? ? ? ? ? ? ? resolve(data);

? ? ? ? ? ? ? ? ? ? }, reject);

? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? resolve(value);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } catch (err) {

? ? ? ? ? ? ? ? reject(err);

? ? ? ? ? ? }

? ? ? ? })

? ? } else {

? ? ? ? promise2 = new MyPromise(function (resolve) {

? ? ? ? ? ? resolve(value);

? ? ? ? })

? ? }

? ? return promise2;

};

MyPromise.reject = function (reason) {

? ? return new MyPromise(function (resolve, reject) {

? ? ? ? reject(reason);

? ? })

};

module.exports = MyPromise;

//這是為了讓代碼能夠測試而開放的接口围肥,詳見promises-aplus-tests中的相關(guān)描述

MyPromise.deferred = MyPromise.defer = function () {

? ? let deferred = {};

? ? deferred.promise = new MyPromise(function (resolve, reject) {

? ? ? ? deferred.resolve = resolve;

? ? ? ? deferred.reject = reject;

? ? });

? ? return deferred

};


尾聲

本文參考了很多資料,如果你看到其他文章有類似的觀點(diǎn)非常正常蜂怎,不過筆者盡量使用了自己的理解去闡述Promise的相關(guān)知識穆刻。如果你發(fā)現(xiàn)本文中有哪些疏漏,歡迎發(fā)私信給我進(jìn)行斧正杠步。同時(shí)也可以在下面留言給我氢伟,我會(huì)一一查看,盡量回復(fù)幽歼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朵锣,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子试躏,更是在濱河造成了極大的恐慌猪勇,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颠蕴,死亡現(xiàn)場離奇詭異泣刹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)犀被,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門椅您,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寡键,你說我怎么就攤上這事掀泳。” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵员舵,是天一觀的道長脑沿。 經(jīng)常有香客問我,道長马僻,這世上最難降的妖魔是什么庄拇? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮韭邓,結(jié)果婚禮上措近,老公的妹妹穿的比我還像新娘。我一直安慰自己女淑,他們只是感情好瞭郑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布为狸。 她就那樣靜靜地躺著蜂绎,像睡著了一般郊艘。 火紅的嫁衣襯著肌膚如雪保屯。 梳的紋絲不亂的頭發(fā)上逼蒙,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天亮隙,我揣著相機(jī)與錄音爪瓜,去河邊找鬼倍踪。 笑死瓣窄,一個(gè)胖子當(dāng)著我的面吹牛笛厦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播俺夕,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼裳凸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了劝贸?” 一聲冷哼從身側(cè)響起姨谷,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎映九,沒想到半個(gè)月后梦湘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡件甥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年捌议,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片引有。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瓣颅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出譬正,到底是詐尸還是另有隱情宫补,我是刑警寧澤檬姥,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站粉怕,受9級特大地震影響健民,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜斋荞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一荞雏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧平酿,春花似錦、人聲如沸悦陋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俺驶。三九已至幸逆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暮现,已是汗流浹背还绘。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栖袋,地道東北人拍顷。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像塘幅,于是被迫代替她去往敵國和親昔案。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355