
倘若你對Promise的使用還不是很熟練,我并不建議你就來實作自己的Promise,
你可以先閱讀
點選進入,希望可以給個star
整體流程的介紹
- 先定義整體結構
- 實作Promise構造函數
- 實作promise.then
- 實作promise.catch
- 實作promise.resovle
- 實作promise.reject
- 實作promise.all
- 實作promise.race
1.定義整體結構
- 先寫出構造函數,将Promise向外暴露
/*
自定義Promise函數子產品:IIFE
*/
(function (window) {
/*
Promise構造函數
executor:執行器函數
*/
function Promise(executor) {
}
// 向外暴露Promise
window.Promise = Promise
})()
- 添加Promise原型對象上的方法
/*
Promise原型對象的then
指定一個成功/失敗的回調函數
傳回一個新的promise對象
*/
Promise.prototype.then = function(onResolved,onRejected){
}
/*
Promise原型對象的.catch
指定一個失敗的回調函數
傳回一個新的promise對象
*/
Promise.prototype.catch = function(onRejected){
}
- 添加Promise函數對象上的方法
/*
Promise函數對象的resovle方法
傳回一個指定結果的promise對象
*/
Promise.resolve = function(value){
}
/*
Promise函數對象的reject方法
傳回一個指定reason的失敗狀态的promise對象
*/
Promise.reject = function(value){
}
/*
Promise函數對象的all方法
傳回一個promise對象,隻有當所有promise都成功時傳回的promise狀态才成功
*/
Promise.all = function(value){
}
/*
Promise函數對象的race方法
傳回一個promise對象,狀态由第一個完成的promise決定
*/
Promise.race = function(value){
}
2. 實作Promise構造函數
- 衆所周知,構造函數裡由resolve和reject方法,而且這兩個方法會被傳入executor,并且executor立即同步執行
/*
Promise構造函數
executor:執行器函數
*/
function Promise(executor) {
function resovle() {
}
function reject() {
}
// 立即同步執行executor
executor(resovle,reject)
}
- 家喻戶曉的是,每個promise都有一個狀态可能為pending或resolved,rejected。是以需要添加個status,此外,當我們這樣使用Promise的時候,
// 例1
var promise = new Promise((resovle,reject)=>{
})
promise.then(resolve=>{},reject=>{})
這時執行到then,promise的狀态還是pending,這時要把值和then裡面的回調函數儲存起來,是以需要個data和callbacks
function Promise(executor) {
var self = self
self.status = 'pending' // 給promise對象指定status屬性,初始值為pending
self.data = undefined // 給promise對象指定一個存儲結果的data
self.callbacks = [] // 每個元素的結構:{onResolved(){},onRejected(){}}
function resovle() {
}
function reject() {
}
// 立即同步執行executor
executor(resovle,reject)
}
- 婦孺皆知的是,在上面的例子1的基礎上,當我們執行resovle(value)時,
// 例2
var promise = new Promise((resolve,reject)=>{
setTimeout(function () {
resolve(1)
})
})
promise.then(resolve=>{},reject=>{})
會把promise對象的status改為resovle,并且把value儲存到data,然後執行之前儲存的callbacks(上面說過當執行到then時,發現目前的promise是pending狀态,會把then裡的回調函數儲存到promise的callbacks裡)。
function resolve(value) {
// 如果目前狀态不是pending,則不執行
if(self.status !== 'pending'){
return
}
// 将狀态改為resolved
self.status = 'resolved'
// 儲存value的值
self.data = value
// 如果有待執行的callback函數,立即異步執行回調函數onResolved
if (self.callbacks.length>0){
setTimeout(()=>{
self.callbacks.forEach(callbackObj=>{
callbackObj.onResolved(value)
})
})
}
}
- 我們還知道,promise的狀态隻能改變一次,是以當執行resolve的時候要判斷是不是promise是不是pending的狀态,否則是不能執行的
function resolve(value) {
// 如果目前狀态不是pending,則不執行
if(this.status !== 'pending'){
return
}
// 将狀态改為resolved
this.status = 'resolved'
// 儲存value的值
this.data = value
// 如果有待執行的callback函數,立即異步執行回調函數onResolved
if (this.callbacks.length>0){
setTimeout(()=>{
this.callbacks.forEach(callbackObj=>{
callbackObj.onResolved(value)
})
})
}
}
- 異曲同工之妙的是reject方法也是這個道理,是以這裡無需贅述
function reject(value) {
// 如果目前狀态不是pending,則不執行
if(self.status !== 'pending'){
return
}
// 将狀态改為resolved
self.status = 'rejected'
// 儲存value的值
self.data = value
// 如果有待執行的callback函數,立即異步執行回調函數onResolved
if (self.callbacks.length>0){
setTimeout(()=>{
self.callbacks.forEach(callbackObj=>{
callbackObj.onRejected(value)
})
})
}
}
- 我們又知道,當在執行executor的時候,如果執行異常的話,這個promise的狀态會直接執行reject方法。
// 例 3
var promise = new Promise((resolve,reject)=>{
執行到這裡出錯了
setTimeout(function () {
resolve(1)
})
})
要實作這個功能,我們可以在executor外讓try catch來捕獲
try{
// 立即同步執行executor
executor(resolve,reject)
}catch (e) { // 如果執行器抛出異常,promise對象變為rejected狀态
reject(e)
}
好了,現在來測試一下,為了測試,我們需要簡單的實作下then,讓它往callbacks裡push then裡的回調函數
Promise.prototype.then = function(onResolved,onRejected){
// 假設目前狀态還是pending狀态,将回調函數儲存起來
this.callbacks.push({
onResolved,
onRejected
})
}
好了,現在測試下,發現成功。
// 例4
let promise = new Promise((resolve,reject)=>{
setTimeout(function () {
// resolve(1)
reject(1)
},100)
})
promise.then(
value=>{
console.log("onResolved:",value);
},
reason=>{
console.log("onRejected:",reason);
}
)
3. 實作then方法
盡人皆知的時,執行到then時,promise可能會是pending狀态,此時就要把then裡的回調函數儲存起來,也可能會是resolved或者rejected狀态,此時要執行onResolved或onRejected方法。
Promise.prototype.then = function(onResolved,onRejected){
var self = this
if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
onResolved(self.data)
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
}
}
而且我們知道,執行完then是要傳回一個新的promise的,而新的promise的狀态則由目前then的執行結果來确定。
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject)=>{
if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
onResolved(self.data)
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
}
})
}
當目前的promise狀态為resolved的時候,則目前執行的onresolved函數由三種情況
- 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
// 例5
let promise = new Promise((resolve,reject)=>{
resolve(1)
})
promise.then(
value=>{
return value
},
reason=>{
console.log("onRejected:",reason);
}
)
是以,我們可以這樣實作
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject)=>{
if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
const result = onResolved(self.data)
if (result instanceof Promise){
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
}
})
}
- 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果,如代碼所示,我們傳回一個新的promise。這個新的promise執行了resolve,是以傳回的promise的狀态是resolved的
// 例6
let promise = new Promise((resolve,reject)=>{
resolve(1)
})
promise.then(
value=>{
return new Promise((resolve,reject)=>{
resolve(2)
})
},
reason=>{
console.log("onRejected:",reason);
}
)
是以我們可以這樣實作
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject)=>{
if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
const result = onResolved(self.data)
if (result instanceof Promise){
// 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
})
}else{
setTimeout(()=>{
onResolved(self.data)
})
}
})
}
在這裡說明一下:
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
由于我們在例6中執行了then裡的resolve函數,
将會導緻
value => {resolve(value)},
這個回調函數的執行,是以會把即将傳回的新的promise的data設定為value,會把狀态設定為resolved。
- 如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected,我們可以用try catch來實作
setTimeout(()=>{
try{
const result = onResolved(self.data)
if (result instanceof Promise){
// 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
}catch (e) {
// 3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
reject(e)
}
})
異曲同工之妙的是當當status === ‘rejected’,道理一樣
setTimeout(()=>{
try{
const result = onRejected(self.data)
if (result instanceof Promise){
// 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
}catch (e) {
// 3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
reject(e)
}
})
到這裡,我們發現當執行resolve的時候,
onResolved(self.data)
和
onRejected(self.data)
執行時也會跟上面一樣的結果,可以說執行回調函數都要做以上判斷,是以我們要将
self.callbacks.push({
onResolved(){onResolved(self.data)},
onRejected(){onRejected(self.data)}
})
改成
if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
onResolved(){
try{
const result = onResolved(self.data)
if (result instanceof Promise){
// 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
}catch (e) {
// 3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
reject(e)
}
},
到此,我們發現,相同的代碼太多了,是以有必要封裝一下
function handle(callback) {
try{
const result = callback(self.data)
if (result instanceof Promise){
// 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
}catch (e) {
// 3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
reject(e)
}
}
這樣以來就清爽了很多
Promise.prototype.then = function(onResolved,onRejected){
var self = this
return new Promise((resolve,reject)=>{
/*
調用指定回調函數的處理,根據執行結果。改變return的promise狀态
*/
function handle(callback) {
try{
const result = callback(self.data)
if (result instanceof Promise){
// 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
}catch (e) {
// 3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
reject(e)
}
}
if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
onResolved(){
handle(onResolved)
},
onRejected(){
handle(onRejected)
}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
handle(onResolved)
})
}else{ // 當status === 'rejected'
setTimeout(()=>{
handle(onRejected)
})
}
})
}
另外,我們還知道,promise會發生直傳透,例如
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
運作結果: 1
解釋:.then 或者 .catch 的參數期望是函數,傳入非函數則會發生值穿透。值傳透可以了解為,當傳入then的不是函數的時候,這個then是無效的。
是以,要實作直傳透這個特性,我們可以這樣實作
添加這兩句來判斷要不要發生值傳透
onResolved = typeof onResolved === 'function'? onResolved: value => value
onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
Promise.prototype.then = function(onResolved,onRejected){
onResolved = typeof onResolved === 'function'? onResolved: value => value
onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
var self = this
return new Promise((resolve,reject)=>{
/*
調用指定回調函數的處理,根據執行結果。改變return的promise狀态
*/
function handle(callback) {
try{
const result = callback(self.data)
if (result instanceof Promise){
// 2. 如果回調函數傳回的是promise,return的promise的結果就是這個promise的結果
result.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else {
// 1. 如果回調函數傳回的不是promise,return的promise的狀态是resolved,value就是傳回的值。
resolve(result)
}
}catch (e) {
// 3.如果執行onResolved的時候抛出錯誤,則傳回的promise的狀态為rejected
reject(e)
}
}
if(self.status === 'pending'){
// promise目前狀态還是pending狀态,将回調函數儲存起來
self.callbacks.push({
onResolved(){
handle(onResolved)
},
onRejected(){
handle(onRejected)
}
})
}else if(self.status === 'resolved'){
setTimeout(()=>{
handle(onResolved)
})
}else{ // 當status === 'rejected'
setTimeout(()=>{
handle(onRejected)
})
}
})
}
3.實作catch方法
婦孺皆知的是,catch方法的作用跟then裡的第二歌回調函數一樣,是以我們可以這樣來實作
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected)
}
天啊,居然這麼簡單
4. 實作Promise.resolve
我們都知道,Promise.resolve方法可以傳三種值
- 不是promise
- 成功狀态的promise
- 失敗狀态的promise
Promise.resolve(1)
Promise.resolve(Promise.resolve(1))
Promise.resolve(Promise.reject(1))
實際上跟實作上面的then時有點像
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
if (value instanceof Promise){
// 如果value 是promise
value.then(
value => {resolve(value)},
reason => {reject(reason)}
)
} else{
// 如果value不是promise
resolve(value)
}
}
}
5.實作Promise.reject
實作這個比較簡單,傳回一個狀态為rejected的promise就好了
/*
Promise函數對象的reject方法
傳回一個指定reason的失敗狀态的promise對象
*/
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
6.實作Promise.all
我們知道,這個方法會傳回一個promise
/*
Promise函數對象的all方法
傳回一個promise對象,隻有當所有promise都成功時傳回的promise狀态才成功
*/
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
})
}
而這個promise的狀态由周遊每個promise産生的結果決定
/*
Promise函數對象的all方法
傳回一個promise對象,隻有當所有promise都成功時傳回的promise狀态才成功
*/
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
// 周遊promises,擷取每個promise的結果
promises.forEach((p,index)=>{
})
})
}
有兩種結果:
- 周遊到有一個promise是reject狀态,則直接傳回的promise狀态為rejected
Promise.all = function(promises){
return new Promise((resolve,reject)=>{
// 周遊promises,擷取每個promise的結果
promises.forEach((p,index)=>{
p.then(
value => {
},
reason => { //隻要有一個失敗,return的promise狀态就為reject
reject(reason)
}
)
})
})
}
- 周遊所有的promise的狀态都為resolved,則傳回的promise狀态為resolved,并且還要每個promise産生的值傳遞下去
Promise.all = function(promises){
const values = new Array(promises.length)
var resolvedCount = 0 //計狀态為resolved的promise的數量
return new Promise((resolve,reject)=>{
// 周遊promises,擷取每個promise的結果
promises.forEach((p,index)=>{
p.then(
value => {
// p狀态為resolved,将值儲存起來
values[index] = value
resolvedCount++;
// 如果全部p都為resolved狀态,return的promise狀态為resolved
if(resolvedCount === promises.length){
resolve(values)
}
},
reason => { //隻要有一個失敗,return的promise狀态就為reject
reject(reason)
}
)
})
})
}
好像可以了,當其實這裡還有一個問題,就是all傳進去的數組不一定都是promise對象,可能是這樣的
all([p,2,3,p])
是以需要把不是promise的數字包裝成promise
Promise.all = function(promises){
const values = new Array(promises.length)
var resolvedCount = 0 //計狀态為resolved的promise的數量
return new Promise((resolve,reject)=>{
// 周遊promises,擷取每個promise的結果
promises.forEach((p,index)=>{
Promise.resolve(p).then(
value => {
// p狀态為resolved,将值儲存起來
values[index] = value
resolvedCount++;
// 如果全部p都為resolved狀态,return的promise狀态為resolved
if(resolvedCount === promises.length){
resolve(values)
}
},
reason => { //隻要有一個失敗,return的promise狀态就為reject
reject(reason)
}
)
})
})
}
7.實作Promise.race
這個方法的實作要比all簡單很多
/*
Promise函數對象的race方法
傳回一個promise對象,狀态由第一個完成的promise決定
*/
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
// 周遊promises,擷取每個promise的結果
promises.forEach((p,index)=>{
Promise.resolve(p).then(
value => {
// 隻要有一個成功,傳回的promise的狀态九尾resolved
resolve(value)
},
reason => { //隻要有一個失敗,return的promise狀态就為reject
reject(reason)
}
)
})
})
}