今天在看文章都時候發現了手寫promise的要求,捫心自問,我靠,我竟然一點思路都沒有。這可不行,必須本身手擼一發,面的對不起本身的左右手。哎,又要學習,好累,不過還好,想一想都2020了,還不會本身手寫promise,着實有點垃圾,仍是趕快搞起來吧。javascript
Promise是一種異步操做的解決方案,將寫法複雜的傳統的回調函數和監聽事件的異步操做,用同步代碼的形式表達出來。避免了多級異步操做的回調函數嵌套。簡單的來講就是爲了解決回調地獄的問題。試想一下,下面這個代碼你看這頭疼不?若是在加幾層呢就會出現金字塔的代碼了,這樣給咱們閱讀代碼和維護代碼形成的很大的困擾。java
function a() {
function b() {
function c() {
function d() {}
d();
}
c();
}
b();
}
a();
複製代碼
咱們將上面的代碼用promise改寫。這樣是否是好多了。promise
a().then(b).then(c).then(d)
複製代碼
咱們按照promiseA+的規範來寫。不知道的同窗能夠看看哦。異步
const PENDING = pending
const FULFILLED = fulfilled
const REJECTED = rejected
class Promise {
constructor(exextuor) {
this.exextuor = exextuor
this.states = PENDING
this.onResolveCallbacks = []
this.onRejectCallbacks = []
}
function resolve() {}
function reject() {}
if(exextuor) {
try {
exextuor(resolve, reject)
}catch(e) {
reject(e)
}
}
}
複製代碼
上面的代碼很簡單,咱們一個resolve的調度棧,一個reject的調度棧,聲明resolve和reject函數,並執行exextuor若是報錯,將reject執行。 ####resolve和reject函數(2.1規範) 一個 promise 有且只有一個狀態(pending,fulfilled,rejected 其中之一) pending 狀態時可能會轉變爲 fulfilled 或 rejected 狀態。fulfilled 狀態時:不能再狀態爲任何其餘狀態,必須有一個 value,且不可改變。 rejected 狀態時:不能再狀態爲任何其餘狀態,必須有一個 reason,且不可改變。函數
function resolve(value) {
if(value instanceof Promise) {
try{
value.then(resolve, reject)
}catch(e) {
reject(e)
}
}
if(this.states = PENDING) {
this.states = FULFILLED
this.value = value
this.onResolveCallbacks.forEach(cb => cb(this.value))
}
}
function reject(reason) {
if(this.states = PENDING) {
this.states = REJECTED
this.reason = reason
this.onRejectCallbacks.forEach(cb => cb(this.reason))
}
}
複製代碼
一個 promise 必須提供一個 then 方法,用來獲取當前或最終的 value 或 reason,一個 promise 的 then 方法接受兩個參數:promise.then(onFulfilled, onRejected)學習
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
onRejected = typeof onRejected === "function" ? onRejected : reason => throw reason
if(this.states = FULFILLED) {
this.x = onFulfilled(this.value)
}
if(this.states = REJECTED) {
this.x = onRejected(this.reason)
}
if(this.states = PENDING) {
this.onResolveCallbacks.push(() => {
this.x = onFulfilled(this.value)
})
this.onRejectCallbacks.push(() => {
this.x = onRejected(this.reason)
})
}
複製代碼
promise 的 then 能夠鏈式調用屢次,若是或當 promise 轉態是 fulfilled 時,全部的 onFulfilled 回調回以他們註冊時的順序依次執行。若是或當 promise 轉態是 rejected 時,全部的 onRejected 回調回以他們註冊時的順序依次執行。測試
let primise2
if(this.states = FULFILLED) {
return primise2 = new Promise((resole, reject) => {
// 若是在執行onFulfilled的時候有異常咱們要將primise2的reject執行
try {
let x = onFulfilled(this.value)
// 對返回值進行處理,由於這個值多是個promise
resolvePromise(primise2, x, resole, reject)
} catch(e) {
reject(e)
}
})
}
複製代碼
注意上面的三個判斷的處理基本同樣,咱們都要對結果處理。resolvePromise一下。 ####resolvePromise(2.3規範) 2.3.1 若是返回的 promise1 和 x 是指向同一個引用(循環引用),則拋出錯誤 2.3.2 若是 x 是一個 promise 實例,則採用它的狀態ui
- 若是 x 是 pending 狀態,那麼保留它(遞歸執行這個 promise 處理程序),直到 pending 狀態轉爲 fulfilled 或 rejected 狀態。
- 若是或當 x 狀態是 fulfilled,resolve 它,而且傳入和 promise1 同樣的值 value。
- 若是或當 x 狀態是 rejected,reject 它,而且傳入和 promise1 同樣的值 reason
const PENDING = pending
const FULFILLED = fulfilled
const REJECTED = rejected
function resolvePromise(promise2, x, resolve, reject) {
if(promise2 === x) {
return reject(new TypeError('循環引入!'))
}
// 用於resolve, reject只調用一次
let called = false
// x是一個promise
if(x instanceof Promise) {
if(x.status === PENDING) {
x.then((y)=>{
// 有可能y仍是一個promise
resolvePromise(promise2, y, resolve, reject)
}, reject)
} else {
x.then(resolve, reject)
}
} else if(x !== null && (typeof x === 'function' || typeof x === 'object')) {
// x是一個thenable的對象
try{
// 有可能在區x.then的時候異常
let then = x.then
if(typeof x === 'function') {
// 這是咱們本身的promise和別人的promise的交互
then.call(x, (z)=>{
// 有可能z仍是一個promise
if(called) return
called = true
resolvePromise(promise2, z,resolve, reject)
}, (err)=>{
if(called) return
called = true
reject(err)
})
}else {
// 說明x是一個普通對象
resolve(x)
}
}catch(e) {
reject(e)
}
} else {
// x是個普通值
if(called) return
called = true
resolve(x)
}
}
複製代碼
catch(onReject) {
this.then(null, onReject)
}
複製代碼
3.1 這裏的 「平臺代碼」是指引擎,環境,和 promise 實現代碼。實際上,這個要求確保 onFulfilled 和 onRejected 都在下一輪的事件循環中(一個新的棧)被異步調用。能夠用宏任務,例如:setTimeout,setImmediate 或者微任務,例如:MutationObsever 或 process.nextTick 實現。 因爲 promise 的實現被當作平臺代碼,因此它自己可能包含一個任務隊列或 「trampoline」 的處理程序this
// 因此咱們要在全部resolve和reject的調用上加一個setTimeout()
// 咱們在resolve函數外面包裹一個
// 同理咱們也要在reject函數包裹一下
function resolve() {
setTimeout(()=>{
// 上面的內容同樣
})
}
複製代碼
Promise.all = function(promises) {
return new Promise((resolve, reject)=>{
let res = []
let count = 0
function done(i, result) {
res[i] = result
if(++count === promises.length) {
resolve(res)
}
}
for(let i = 0; i<promises.length; i++) {
done.call(null, i,res)
}
}, reject)
}
複製代碼
Promise.deferred = Promise.deferred = function() {
let defer = {}
defer.promise = new Promise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
複製代碼
咱們測試用用例測試。基本全部的用例所有經過。好了這樣一個promise基本實現。