JaveScript中的Promise對(duì)象


前言

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

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

相關(guān)參考

JavaScript Promise迷你書

Promise/A+規(guī)范



正文

1.Promise簡(jiǎn)介

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

什么是Promise?

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

為什么要使用Promise?

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

例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è)簡(jiǎn)單的異步操作處理過(guò)程,asyncFunc就是一個(gè)異步的函數(shù),執(zhí)行后通過(guò)setTimeout方法在2秒返回了一個(gè)值,而foo則是一個(gè)回調(diào)函數(shù),通過(guò)傳入異步函數(shù)并且在返回結(jié)果后被調(diào)用的方式獲取異步操作的結(jié)果。這里的回調(diào)函數(shù)就如同一個(gè)事先的約定,在異步操作返回結(jié)果后立即被實(shí)現(xiàn)。

那么,既然js中已經(jīng)有處理異步事件的方法,為何還要引入Promise這個(gè)新的方式呢?實(shí)際上,上面這段代碼只是簡(jiǎn)單展示下回調(diào)函數(shù)的基礎(chǔ)使用,而在真正的使用場(chǎng)景中,我們不得不面對(duì)各種十分復(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請(qǐng)求數(shù)據(jù)。在url1請(qǐng)求的回調(diào)中使用了foo和bar兩個(gè)函數(shù),而foo中又發(fā)送了url2,url3的請(qǐng)求。。。這樣數(shù)層嵌套下來(lái),最終導(dǎo)致代碼非常的不直觀,維護(hù)起來(lái)難度也直線上升,形成常說(shuō)的“回調(diào)地獄”。

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

2. Promise的特性及使用

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

? 這段定義有兩個(gè)重點(diǎn):1.Promise是一個(gè)對(duì)象或函數(shù)? 2.它有一個(gè)then方法,能夠獲取prmose的最終結(jié)果。下面我們就來(lái)實(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ù)雜的場(chǎng)景下才能更好的發(fā)揮。我們先針對(duì)這個(gè)簡(jiǎn)單的例子來(lái)講解下Promise的使用

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

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

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

一個(gè)Promise對(duì)象在實(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對(duì)象都只能處于以上其中狀態(tài)的一種,當(dāng)Promise對(duì)象處于Pending狀態(tài)時(shí),它可以轉(zhuǎn)化成Fulfilled 或Rejected 狀態(tài),而當(dāng)Promise對(duì)象處于Fulfilled 或Rejected狀態(tài)時(shí),它不能再轉(zhuǎn)化成其他狀態(tài)。

可以用一張圖來(lái)直白的表示上面這段話


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

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

resolve()

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

reject()

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

Promise.prototype.then()

then方法是Promise對(duì)象方法的重中之重,它是Promise實(shí)例的方法,用來(lái)注冊(cè)Promise對(duì)象成功時(shí)執(zhí)行的回調(diào)函數(shù)(onFulfilled)和失敗時(shí)執(zhí)行的回調(diào)函數(shù)(onRejected)。一個(gè)then方法的返回值仍然是一個(gè)Promsie對(duì)象。因此,then方法支持鏈?zhǔn)秸{(diào)用,也就是一個(gè)一個(gè)then方法的返回值可以繼續(xù)調(diào)用then。而相鏈接的then方法中,在上一個(gè)then方法的onFulfilled或onRejected回調(diào)函數(shù)中通過(guò) return value(reason)的方式,把這個(gè)結(jié)果作為下一個(gè)then中的回調(diào)函數(shù)的參數(shù)被接收。onFulfilled和onRejected函數(shù)的返回值可以是任何javascript值,甚至一個(gè)Promise對(duì)象的成功或失敗時(shí)的回調(diào)函數(shù)可以返回一個(gè)新的Promise對(duì)象。這樣的特性使得例2中那種復(fù)雜的異步事件嵌套的場(chǎng)景處理得以簡(jiǎn)化。下面是使用Promise來(lái)重寫的例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注冊(cè)的onFulfilled和onRejected函數(shù)會(huì)按照注冊(cè)的順序執(zhí)行。

Promise.prototype.catch()

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

new Promsie.then(null,function(){

? ? do something...

})

Promise.all()

all方法是Promsie對(duì)象的靜態(tài)方法,使用方式是 Promise.all()。all方法接收的參數(shù)為一個(gè)包含數(shù)個(gè)Promise對(duì)象實(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í)例的順序來(lái)排列的。

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

Promise.resolve()

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

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

Promise.reject()

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

new Promise(function(resolve,reject){

? ? reject(reason);

})

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

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

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

Compliances tests for Promises/A+

使用的方法很簡(jiǎn)單

npm i -g promises-aplus-tests

promises-aplus-tests Promise.js

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

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


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)一性和注冊(cè)的回調(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對(duì)象,即一個(gè)擁有then方法的對(duì)象,那么使用它的then方法去獲得它的最終返回值。目的是為了兼容其他Promise庫(kù)

? ? 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方法注冊(cè)的時(shí)候如果Promise的狀態(tài)已經(jīng)改變,那么立即執(zhí)行對(duì)應(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('無(wú)法遍歷的類型!'));

? ? ? ? }

? ? });

? ? 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('無(wú)法遍歷的類型!'));

? ? ? ? }

? ? });

? ? 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;

//這是為了讓代碼能夠測(cè)試而開放的接口,詳見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)非常正常,不過(guò)筆者盡量使用了自己的理解去闡述Promise的相關(guān)知識(shí)。如果你發(fā)現(xiàn)本文中有哪些疏漏,歡迎發(fā)私信給我進(jìn)行斧正。同時(shí)也可以在下面留言給我,我會(huì)一一查看,盡量回復(fù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374