ECMAscript 6 原生提供了 Promise 對象,用來優雅解決異步問題,避免回調噩夢。
Promise對象有三種狀态:
- pending: 初始狀态,不是成功或失敗狀态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失敗。
實作Promise:
目标:可以實作優雅的異步處理方式,以下是想要達到最終狀态:
new MyPromise(function(resolve){
setTimeout(()=>{
resolve(122121)
},1000)
}).then(r=>{
console.log(r)
}).catch(e=>{
console.log(e)
})
第一步:定義狀态,初始化Promise外殼
- 定義三種狀态常量、增加判斷是否為函數方法(Promise參數必須是函數)
- 初始化狀态、傳回值、成功或失敗隊列
- 初始化then、catch、resolve、reject、all、race、finally方法(簡易Promise隻需then和catch即可)
// 判斷變量否為function
const isFunction = variable => typeof variable === 'function'
// 定義Promise的三種狀态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 初始化狀态
this._status = PENDING
// 初始化傳回值
this._value = undefined
// 初始化成功回調函數隊列
this._fulfilledQueues = []
// 初始化失敗回調函數隊列
this._rejectedQueues = []
// todo
}
then (onFulfilled, onRejected) {
// todo
}
// 添加catch方法
catch (onRejected) {
// todo
}
// 添加靜态resolve方法
static resolve (value) {
// todo
}
// 添加靜态reject方法
static reject (value) {
// todo
}
// 添加靜态all方法
static all (list) {
// todo
}
// 添加靜态race方法
static race (list) {
// todo
}
finally (cb) {
// todo
}
}
第二步:實作傳遞參數resolve和reject
- 執行回調函數handle方法,并傳入resolve和reject方法,友善回調函數傳值
- 了解resolve、reject方法,在傳入的函數中,resolve可以把promise的狀态改為FULFILLED,reject改為REJECTED
// 判斷變量否為function
const isFunction = variable => typeof variable === 'function'
// 定義Promise的三種狀态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 初始化狀态
this._status = PENDING
// 初始化傳回值
this._value = undefined
// 初始化成功回調函數隊列
this._fulfilledQueues = []
// 初始化失敗回調函數隊列
this._rejectedQueues = []
// 執行回調函數handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
}
resolve函數待辦項
- 修改PENDING狀态為FULFILLED狀态
- 執行成功隊列的函數
- 指派this._value -- resolve函數傳遞的值
_resolve (val) {
if (this._status !== PENDING) return
this._status = FULFILLED
// 依次執行成功隊列中的函數,并清空隊列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
this._value = val
runFulfilled(val)
}
resolve函數優化:
- 支援同步Promise
- 支援鍊式調用
_resolve (val) {
const run = () => {
if (this._status !== PENDING) return
this._status = FULFILLED
// 依次執行成功隊列中的函數,并清空隊列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次執行失敗隊列中的函數,并清空隊列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error)
}
}
/* 如果resolve的參數為Promise對象,則必須等待該Promise對象狀态改變後,
目前Promsie的狀态才會改變,且狀态取決于參數Promsie對象的狀态
*/
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
runFulfilled(value)
}, err => {
this._value = err
runRejected(err)
})
} else {
this._value = val
runFulfilled(val)
}
}
// 為了支援同步的Promise,這裡采用異步調用
setTimeout(run, 0)
}
reject函數待辦項
- 修改PENDING狀态為REJECTED狀态
- 執行失敗隊列的函數
- 指派this._value -- reject函數傳遞的值
_reject (err) {
if (this._status !== PENDING) return
// 依次執行失敗隊列中的函數,并清空隊列
const run = () => {
this._status = REJECTED
this._value = err
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err)
}
}
// 為了支援同步的Promise,這裡采用異步調用
setTimeout(run, 0)
}
then函數待辦項
- 當狀态為pengding時,将then方法加入執行隊列等待執行
- 當狀态已經改變時,立即執行對應的回調函數
then (onFulfilled, onRejected) {
const { _value, _status } = this
// 傳回一個新的Promise對象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封裝一個成功時執行的函數
let fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value)
} else {
let res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函數執行出錯,新的Promise對象的狀态為失敗
onRejectedNext(err)
}
}
// 封裝一個失敗時執行的函數
let rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函數執行出錯,新的Promise對象的狀态為失敗
onRejectedNext(err)
}
}
switch (_status) {
// 當狀态為pending時,将then方法回調函數加入執行隊列等待執行
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 當狀态已經改變時,立即執行對應的回調函數
case FULFILLED:
fulfilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}
其他函數實作起來比較簡單,其實就是各種文法糖。
catch函數:
- 直接指向then的第二個方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
resolve函數
- new Promise((resolve)=>resolve())的文法糖
static resolve (value) {
// 如果參數是MyPromise執行個體,直接傳回這個執行個體
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
reject函數
- new Promise((resolve, reject)=>reject())的文法糖
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
all函數
- 所有Promise都完成執行函數
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 傳回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 數組參數如果不是MyPromise執行個體,先調用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有狀态都變成fulfilled時傳回的MyPromise狀态就變成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一個被rejected時傳回的MyPromise狀态就變成rejected
reject(err)
})
}
})
}
race函數
- 隻要有一個執行個體率先改變狀态,新的MyPromise的狀态就跟着改變
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 隻要有一個執行個體率先改變狀态,新的MyPromise的狀态就跟着改變
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally函數
- 無論成功或失敗都會執行的函數
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
}
完整源碼:
// 判斷變量否為function
const isFunction = variable => typeof variable === 'function'
// 定義Promise的三種狀态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加狀态
this._status = PENDING
// 添加狀态
this._value = undefined
// 添加成功回調函數隊列
this._fulfilledQueues = []
// 添加失敗回調函數隊列
this._rejectedQueues = []
// 執行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle時執行的函數
_resolve (val) {
const run = () => {
if (this._status !== PENDING) return
this._status = FULFILLED
// 依次執行成功隊列中的函數,并清空隊列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次執行失敗隊列中的函數,并清空隊列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error)
}
}
/* 如果resolve的參數為Promise對象,則必須等待該Promise對象狀态改變後,
目前Promsie的狀态才會改變,且狀态取決于參數Promsie對象的狀态
*/
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
runFulfilled(value)
}, err => {
this._value = err
runRejected(err)
})
} else {
this._value = val
runFulfilled(val)
}
}
// 為了支援同步的Promise,這裡采用異步調用
setTimeout(run, 0)
}
// 添加reject時執行的函數
_reject (err) {
if (this._status !== PENDING) return
// 依次執行失敗隊列中的函數,并清空隊列
const run = () => {
this._status = REJECTED
this._value = err
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err)
}
}
// 為了支援同步的Promise,這裡采用異步調用
setTimeout(run, 0)
}
// 添加then方法
then (onFulfilled, onRejected) {
const { _value, _status } = this
// 傳回一個新的Promise對象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封裝一個成功時執行的函數
let fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value)
} else {
let res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函數執行出錯,新的Promise對象的狀态為失敗
onRejectedNext(err)
}
}
// 封裝一個失敗時執行的函數
let rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果目前回調函數傳回MyPromise對象,必須等待其狀态改變後在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//否則會将傳回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函數執行出錯,新的Promise對象的狀态為失敗
onRejectedNext(err)
}
}
switch (_status) {
// 當狀态為pending時,将then方法回調函數加入執行隊列等待執行
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 當狀态已經改變時,立即執行對應的回調函數
case FULFILLED:
fulfilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加靜态resolve方法
static resolve (value) {
// 如果參數是MyPromise執行個體,直接傳回這個執行個體
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 添加靜态reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
// 添加靜态all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 傳回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 數組參數如果不是MyPromise執行個體,先調用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有狀态都變成fulfilled時傳回的MyPromise狀态就變成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一個被rejected時傳回的MyPromise狀态就變成rejected
reject(err)
})
}
})
}
// 添加靜态race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 隻要有一個執行個體率先改變狀态,新的MyPromise的狀态就跟着改變
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
}
}