JavaScript手寫Promise核心原理
準(zhǔn)備
- 首先,
promise有三種狀態(tài):pendingfulfilledrejected; promise在實(shí)例化操作中, 有兩個(gè)改變狀態(tài)的方法,分別為resolve,reject;promise有很多方法,詳情請見mdn, 本篇文章先實(shí)現(xiàn)promise的核心api:then和catch;
我們使用 es6 提供的 class 來實(shí)現(xiàn)
class MyPromise {
// 準(zhǔn)備三個(gè)狀態(tài)
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING; // 表示promise的狀態(tài)
this.value = null; // 表示promise的值
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
resolve() {
}
reject() {
}
}
在這里 executor 就是傳遞過來的函數(shù),可以接收 resolve和reject,這里將內(nèi)部的兩個(gè)方法給傳入,如果在調(diào)用的過程中報(bào)錯(cuò)了會調(diào)用reject方法
完善 resolve/reject
他們做的工作分為以下幾部
- 將狀態(tài)改為
pending為fulfilled或rejected - 可以接受一個(gè)值為當(dāng)前的
promise的value
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value
}
}
reject(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = value
}
}
then
then 函數(shù)可以接受兩個(gè)參數(shù),分別為成功的回調(diào)函數(shù)和失敗的回調(diào)函數(shù),并且回調(diào)函數(shù)的默認(rèn)為一個(gè)函數(shù)
- 狀態(tài)為
fulfilled執(zhí)行第一個(gè)回調(diào),rejected執(zhí)行第二個(gè)回調(diào) - 回調(diào)函數(shù)中給傳入當(dāng)前的
value then的執(zhí)行為異步的
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value
}
if (typeof onFulfilled !== 'function') {
onRejected = value => value
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
onFulfilled(this.value)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
onRejected(this.value)
})
}
}
驗(yàn)證一下:
console.log(1)
new MyPromise((resolve, reject) => {
console.log(2)
resolve('成功')
}).then(res => console.log(res))
console.log(3)
// 打印 1 2 3 成功
當(dāng)promise里面有異步代碼的時(shí)候,這個(gè)時(shí)候運(yùn)行到.then方法 狀態(tài)為pending,下來增加一下異步任務(wù)的處理
異步處理
當(dāng)狀態(tài)為pending的時(shí)候,表示執(zhí)行的是異步任務(wù),這個(gè)時(shí)候我們可以增加一個(gè)callback,把異步執(zhí)行的內(nèi)容添加到這個(gè)callback中,當(dāng)執(zhí)行完異步代碼的時(shí)候,會執(zhí)行異步函數(shù)的callback的任務(wù)
constructor(executor) {
// ...
this.callbacks = []; // 用來存儲回調(diào)函數(shù)的容器
// ...
}
resolve(value) {
// ...
this.callbacks.forEach(({ onFulfilled }) => onFulfilled(value))
// 當(dāng)執(zhí)行到這里的時(shí)候 如果有onFulfilled 就說明已經(jīng)執(zhí)行完then方法給容器添加內(nèi)容了。把resolve的值傳遞給onFulfilled
}
reject(value) {
// ...
this.callbacks.forEach(({ onRejected }) => onRejected(value))
// 當(dāng)執(zhí)行到這里的時(shí)候 如果有onRejected 就說明已經(jīng)執(zhí)行完then方法給容器添加內(nèi)容了。把reject的值傳遞給onFulfilled
}
then(onFulfilled, onRejected) {
// ...
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
setTimeout(() => {
onFulfilled(value)
})
},
onRejected: value => {
setTimeout(() => {
onRejected(value)
})
}
})
}
}
驗(yàn)證一下:
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
})
}).then(res => console.log(res))
// 打印 成功
then 函數(shù)可以鏈?zhǔn)秸{(diào)用,接下來我們完善一下
鏈?zhǔn)秸{(diào)用
鏈?zhǔn)秸{(diào)用的核心就是返回一個(gè)新的 promise,當(dāng)成功調(diào)用的時(shí)候調(diào)用新的promise的resolve,失敗reject,并且鏈?zhǔn)秸{(diào)用會把前一個(gè)的返回值當(dāng)作下一個(gè)的 resolve 的狀態(tài)
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value
}
if (typeof onFulfilled !== 'function') {
onRejected = value => value
}
return new MyPromise((resolve, reject) => {
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
const result = onFulfilled(this.value)
resolve(result)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
const result = onRejected(this.value)
resolve(result)
})
}
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
setTimeout(() => {
const result = onFulfilled(value)
resolve(result)
})
},
onRejected: value => {
setTimeout(() => {
const result = onRejected(value)
resolve(result)
})
}
})
}
})
}驗(yàn)證一下:
new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('失敗')
})
}).then(res => res, err => err).then(res => console.log(res))
// 打印 失敗
如果.then的回調(diào)函數(shù)返回的是promise的情況也要做個(gè)處理
邊界處理
實(shí)現(xiàn)前:
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
})
}).then(
res => new MyPromise((resolve, reject) => {
resolve(res)
}),
err => err
).then(res => console.log(res))
// 打印 { "status": "fulfilled", "value": "成功", "callbacks": [] }
當(dāng)判斷返回值為 MyPromise 的時(shí)候,需要手動調(diào)用 .then 的方法取他的值,并且吧當(dāng)前的 promise 的改變狀態(tài)的函數(shù)透出給 then 方法
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value
}
if (typeof onFulfilled !== 'function') {
onRejected = value => value
}
return new MyPromise((resolve, reject) => {
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
const result = onFulfilled(this.value)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
const result = onRejected(this.value)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
})
}
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
setTimeout(() => {
const result = onFulfilled(value)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
})
},
onRejected: value => {
setTimeout(() => {
const result = onRejected(value)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
})
}
})
}
})
}
驗(yàn)證:
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
})
}).then(
res => new MyPromise((resolve, reject) => {
resolve(res)
}),
err => err
).then(res => console.log(res))
// 打印 成功
到這里 .then 方法就實(shí)現(xiàn)差不多了,接下來實(shí)現(xiàn) catch 方法
catch
catch 方法可以處理拒絕的狀態(tài)和錯(cuò)誤的狀態(tài):
catch(onFulfilled) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value
}
return new MyPromise((resolve, reject) => {
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
const result = onFulfilled(this.value)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
})
}
})
}
驗(yàn)證:
new MyPromise((resolve, reject) => {
reject('失敗')
}).catch(res=> console.log(res))
// 打印 失敗
道理其實(shí)和 then 是相同的,到這里主功能基本上就差不多了,但是有很多重復(fù)的地方,優(yōu)化一下
優(yōu)化后完整代碼
class MyPromise {
// 準(zhǔn)備三個(gè)狀態(tài)
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING; // 表示promise的狀態(tài)
this.value = null; // 表示promise的值
this.callbacks = [];
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
console.log(error)
this.reject(error)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value
}
this.callbacks.forEach(({ onFulfilled }) => onFulfilled(value))
}
reject(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = value
}
this.callbacks.forEach(({ onRejected }) => onRejected(value))
}
parse({ callback, resolve, reject, value = this.value }) {
setTimeout(() => {
const result = callback(value)
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
})
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value
}
if (typeof onFulfilled !== 'function') {
onRejected = value => value
}
return new MyPromise((resolve, reject) => {
if (this.status === MyPromise.FULFILLED) {
this.parse({ callback: onFulfilled, resolve, reject })
}
if (this.status === MyPromise.REJECTED) {
this.parse({ callback: onRejected, resolve, reject })
}
if (this.status === MyPromise.PENDING) {
this.callbacks.push({
onFulfilled: value => {
this.parse({ callback: onFulfilled, resolve, reject, value })
},
onRejected: value => {
this.parse({ callback: onRejected, resolve, reject, value })
}
})
}
})
}
catch(onFulfilled) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value
}
return new MyPromise((resolve, reject) => {
if (this.status === MyPromise.REJECTED) {
this.parse({ callback: onFulfilled, resolve, reject })
}
})
}
}到此這篇關(guān)于JavaScript手寫Promise核心原理的文章就介紹到這了,更多相關(guān)JavaScript Promise內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決html按鈕切換綁定不同函數(shù)后點(diǎn)擊時(shí)執(zhí)行多次函數(shù)問題
這篇文章主要介紹了如何解決html按鈕切換綁定不同函數(shù)后點(diǎn)擊時(shí)執(zhí)行多次函數(shù)問題,需要的朋友可以參考下2014-05-05
一道超經(jīng)典js面試題Foo.getName()的故事
Foo.getName算是一道比較老的面試題了,大致百度了一下在17年就有相關(guān)文章在介紹它,下面這篇文章主要給大家介紹了關(guān)于一道超經(jīng)典js面試題Foo.getName()的相關(guān)資料,需要的朋友可以參考下2022-03-03
JS實(shí)現(xiàn)title標(biāo)題欄文字不間斷滾動顯示效果
這篇文章主要介紹了JS實(shí)現(xiàn)title標(biāo)題欄文字不間斷滾動顯示效果,通過javascript時(shí)間函數(shù)定時(shí)操作動態(tài)修改頁面元素實(shí)現(xiàn)滾動效果,需要的朋友可以參考下2016-09-09
表單元素值獲取方式j(luò)s及java方式的簡單實(shí)例
下面小編就為大家?guī)硪黄韱卧刂但@取方式j(luò)s及java方式的簡單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10
微信小程序h5頁面跳轉(zhuǎn)小程序的超詳細(xì)講解
開發(fā)中涉及到一個(gè)需求,就是從一個(gè)預(yù)約票購買的頁面需要跳轉(zhuǎn)到?小程序,下面這篇文章主要給大家介紹了關(guān)于微信小程序h5頁面跳轉(zhuǎn)小程序的超詳細(xì)講解,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
javascript動態(tài)向網(wǎng)頁中添加表格實(shí)現(xiàn)代碼
動態(tài)向網(wǎng)頁中添加表格的方法有很多,本文為大家介紹下利用javascript是如何實(shí)現(xiàn)的2014-02-02
Javascript點(diǎn)擊其他任意地方隱藏關(guān)閉DIV實(shí)例
這篇文章主要分享一個(gè)Javascript點(diǎn)擊其他任意地方隱藏關(guān)閉DIV實(shí)例,需要的朋友可以參考下。2016-06-06

