promise是什麼
以下來自百度
所謂Promise,簡單說就是一個容器,裏面保存着某個未來纔會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。
Promise對象有以下兩個特點。
(1)對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變爲Resolved和從Pending變爲Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操作更加容易。
promise圖解
promise有兩大狀態,未決狀態和已決狀態
- 未決狀態:PENDING
- 已決狀態:RESOLVED/REJECTED
promise中有兩個方法可以將未決狀態推向已決狀態
- resolve:將狀態從PENDING推向RESOLVED
- reject:將狀態從PENDING推向REJECTED
promise還有兩個列表用來存儲成功後的回調函數與失敗的回調函數
thenables/catchables
圖解如下
手動封裝MyPromise
既然明白了Promise的原理,那麼promise內部就要有4個變量來存儲相應的狀態
在這裏封裝的MyPromise沒有添加鏈式調用的功能,因爲鏈式調用還沒有搞懂。。。如果你明白鏈式調用的原理,請聯繫15011177648(微信同號)
- 當前狀態
- 值(後續處理函數的參數)
- 成功狀態回調列表
- 失敗狀態回調列表
class MyPromise {
// 接受一個函數
constructor(fun) {
// 定義後續處理參數
this.value = undefined;
// 定義promise狀態,最初是未決
this.status = "PENDDING";
// 定義成功後續處理函數列表
this.thenableList = [];
// 定義失敗後續處理函數列表
this.catchableList = [];
// 將狀態推向成功的函數
let resolve = (value) => {
this.changeStatus('RESOLVED', value);
// 循環執行成功隊列的函數
this.thenableList.forEach((item) => {
item(this.value)
})
}
// 將狀態推向失敗的函數
let reject = (value) => {
this.changeStatus('REJECTED', value);
// 循環執行失敗隊列的函數
this.catchableList.forEach((item) => {
item(this.value)
})
}
// 嘗試執行傳進來的函數
try {
fun(resolve, reject);
} catch (e) {
reject(e)
}
}
// 改變狀態
changeStatus(newStatus, value) {
// 由於狀態是不可逆的,所以先盤算是不是未決,不是直接返回
if (this.status != "PENDDING") return
this.status = newStatus;
this.value = value
}
// 後續處理
settleHandle(newStatus, able, list) {
// 判斷參數是否爲函數
if (typeof able != 'function') {
return
}
// 判斷狀態
if (this.status == newStatus) {
// 在Promise中,後續處理函數應該是放在事件微隊列,但是jsAPI無法將事件放入微隊列,這裏使用setTimeout的宏隊列代替
// 如果已經是已決了,直接執行
setTimeout(() => {
able(this.value)
}, 0)
} else {
// 不是已決的話就放進任務隊列,等待狀態改變
list.push(able);
}
}
// 添加成功處理函數
then(thenable, catchable) {
this.settleHandle('RESOLVED', thenable, this.thenableList);
// 由於成功處理函數可接受兩個參數,所以這裏也要調一下失敗處理函數
this.catch(catchable)
}
// 添加失敗處理函數
catch(catchable) {
this.settleHandle('REJECTED', catchable, this.catchableList)
}
}
私有化變量
在上面的promise中,完成了promise的基本功能,但是真正的Promise的某些變量,外接是訪問不到的(除了then,catch,finally都訪問不到),所以我們使用閉包加符號來私有化變量。
有關符號的使用,我會再寫一篇博客來介紹
const MyPromise = (() => {
const PENDING = 'pending',
RESOLVED = 'resolved',
REJECTED = 'rejected',
promiseValue = Symbol('後續處理參數'),
promiseStatus = Symbol('當前狀態'),
thenables = Symbol('成功後的調用函數列表'),
catchables = Symbol('成功後的調用函數列表'),
changeStatus = Symbol('改變狀態'),
settleHandle = Symbol('後續處理');
return class MyPromise {
// 新狀態, then的參數
[changeStatus](newStatus, result, queue) {
if (this[promiseStatus] !== PENDING) {
//狀態無法變更
return;
}
this[promiseStatus] = newStatus;
this[promiseValue] = result;
queue.forEach(handle => {
handle(this[promiseValue])
});
}
constructor(executor) {
this[promiseValue] = undefined;
this[promiseStatus] = PENDING;
this[thenables] = [];
this[catchables] = [];
const resolve = (result) => {
this[changeStatus](RESOLVED, result, this[thenables]);
}
const reject = (err) => {
this[changeStatus](REJECTED, err, this[catchables]);
}
try {
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// 後續事件, 立即執行狀態, 後續事件列表
[settleHandle](handle, status, queueList) {
if (typeof handle !== 'function') {
return
}
if (this[promiseStatus] == status) {
setTimeout(() => {
handle(this[promiseValue])
}, 0)
} else {
queueList.push(handle);
}
}
then(thenable, catchable) {
this[settleHandle](thenable, RESOLVED, this[thenables])
this.catch(catchable)
}
catch(catchable) {
this[settleHandle](catchable, REJECTED, this[catchables])
}
}
})()
Promise的靜態方法
promise還有一些靜態方法(這些靜態方法怎麼用就不做贅述了)
- all
- rece
- resolve
- reject
現在我們來實現一下這四個靜態方法
const MyPromise = (() => {
const PENDING = 'pending',
RESOLVED = 'resolved',
REJECTED = 'rejected',
promiseValue = Symbol('後續處理參數'),
promiseStatus = Symbol('當前狀態'),
thenables = Symbol('成功後的調用函數列表'),
catchables = Symbol('成功後的調用函數列表'),
changeStatus = Symbol('改變狀態'),
settleHandle = Symbol('後續處理');
return class MyPromise {
// ...(略)
// 靜態方法-resolve
static resolve(data) {
if (data instanceof MyPromise) {
return data
} else {
return MyPromise(resolve => {
resolve(data)
})
}
}
// 靜態方法-reject
static reject(reason) {
return MyPromise((resolve, reject) => {
reject(reason)
})
}
// 靜態方法-all
static all(ProList) {
return new MyPromise((resolve, reject) => {
// 給每個promise對象註冊後續處理
// 成功就查看是否全部成功,全部成功讓all的promise也成功
// 失敗就直接失敗
let result = ProList.map((p) => {
let obj = {
value: undefined,
isResolved: false
}
p.then((data) => {
obj.value = data;
obj.isResolved = true;
// then的後續處理函數是放在微隊列,所以當後續處理函數執行時result已經有值了
let filterProList = result.filter((item) => !item.isResolved);
if (filterProList.length == 0) {
resolve(result.map(r => r.value))
}
}, (reason) => {
reject(reason)
})
return obj
})
})
}
// 靜態方法-race
static race(ProList) {
// 給每個promise註冊後續處理函數,變爲已決就將rece的promise推向對應狀態
return new MyPromise((resolve, reject) => {
ProList.forEach((p) => {
p.then(data => {
resolve(data)
},err => {
reject(err)
})
})
})
}
}
})()