Promise
promise-a-plus
const PENDING = 'PENDING'
const REJECTED = 'REJECTED'
const RESOLVED = 'RESOLVED'
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
// ES6 規範寫法 無法通過Promise/A+測試
// return reject('[TypeError: Chaining cycle detected for promise #<Promise>]')
// Promise/A+ 規範寫法
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
let called
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
const then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (called) return
called = true
reject(e)
})
} else {
resolve(x)
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.status = PENDING
this.value = undefined
this.onResolvedCallbackArr = []
this.onRejectedCallbackArr = []
const resolve = (value) => {
// resolve中使用模板字符串,無法通過Promise/A+測試
// console.log(`${value}`)
if (value === this) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (value instanceof Promise) {
return value.then(resolve, reject)
}
// resolve解析thenable對象是ES6的功能,無法通過Promise/A+測試
// if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
// typeof value.then === 'function') {
// return process.nextTick(() => {
// try {
// value.then(resolve, reject)
// } catch (error) {
// reject(error)
// }
// })
// }
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onResolvedCallbackArr.forEach(cb => cb())
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.value = reason
this.status = REJECTED
this.onRejectedCallbackArr.forEach(cb => cb())
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
const promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
const x = onResolved(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === PENDING) {
this.onResolvedCallbackArr.push(() => {
setTimeout(() => {
try {
const x = onResolved(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejectedCallbackArr.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise2
}
//==============以下非 Promise/A+ 規範中的內容==================
catch(onRejected) {
return this.then(null, onRejected)
}
finally(callback) {
return this.then(value => {
return Promise.resolve(callback()).then(() => value)
}, error => {
return Promise.resolve(callback()).then(() => { throw error })
})
}
static resolve(value) {
if (value instanceof Promise) return value
return new Promise((resolve, reject) => {
if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
typeof value.then === 'function') {
process.nextTick(() => {
try {
value.then(resolve, reject)
} catch (error) {
reject(error)
}
})
} else {
resolve(value)
}
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
if (promises == undefined || !promises[Symbol.iterator]) {
const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}`
return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`))
}
if (promises.length === 0) return resolve([])
let index = 0
const resultArr = []
const processValue = (i, value) => {
resultArr[i] = value
if (++index === promises.length) {
return resolve(resultArr)
}
}
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(value => {
processValue(i, value)
}, error => {
return reject(error)
})
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
if (promises == undefined || !promises[Symbol.iterator]) {
const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}`
return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`))
}
if (promises.length === 0) return
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(value => {
return resolve(value)
}, error => {
return reject(error)
})
}
})
}
}
Promise.defer = Promise.deferred = function () {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
promise-es6
/**
* ES6實現的Promise,在Promise/A+基礎上擴展了很多功能,無法通過爲Promise/A+寫的測試
*/
const PENDING = 'PENDING'
const REJECTED = 'REJECTED'
const RESOLVED = 'RESOLVED'
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
// ES6 規範寫法 無法通過Promise/A+測試
return reject('[TypeError: Chaining cycle detected for promise #<Promise>]')
}
let called
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
const then = x.then
if (typeof then === 'function') {
// then.call(x, y => {
// if (called) return
// called = true
// resolvePromise(promise2, y, resolve, reject)
// }, e => {
// if (called) return
// called = true
// reject(e)
// })
// 模擬ES6的行爲(異步調用thenable的then方法)。無法通過Promise/A+測試
process.nextTick(() => {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (called) return
called = true
reject(e)
})
})
} else {
resolve(x)
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.status = PENDING
this.value = undefined
this.onResolvedCallbackArr = []
this.onRejectedCallbackArr = []
const resolve = (value) => {
if (value === this) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (value instanceof Promise) {
return value.then(resolve, reject)
}
// resolve解析thenable對象是ES6 Promise的功能,無法通過Promise/A+測試
if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
typeof value.then === 'function') {
return process.nextTick(() => {
try {
value.then(resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onResolvedCallbackArr.forEach(cb => cb())
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.value = reason
this.status = REJECTED
this.onRejectedCallbackArr.forEach(cb => cb())
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }
const promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
process.nextTick(() => {
try {
const x = onResolved(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === REJECTED) {
process.nextTick(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === PENDING) {
this.onResolvedCallbackArr.push(() => {
process.nextTick(() => {
try {
const x = onResolved(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.onRejectedCallbackArr.push(() => {
process.nextTick(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
})
return promise2
}
//==============以下非 Promise/A+ 規範中的內容==================
catch(onRejected) {
return this.then(null, onRejected)
}
finally(callback) {
return this.then(value => {
return Promise.resolve(callback()).then(() => value)
}, error => {
return Promise.resolve(callback()).then(() => { throw error })
})
}
static resolve(value) {
if (value instanceof Promise) return value
return new Promise((resolve, reject) => {
if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
typeof value.then === 'function') {
process.nextTick(() => {
try {
value.then(resolve, reject)
} catch (error) {
reject(error)
}
})
} else {
resolve(value)
}
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
if (promises == undefined || !promises[Symbol.iterator]) {
const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}`
return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`))
}
if (promises.length === 0) return resolve([])
let index = 0
const resultArr = []
const processValue = (i, value) => {
resultArr[i] = value
if (++index === promises.length) {
return resolve(resultArr)
}
}
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(value => {
processValue(i, value)
}, error => {
return reject(error)
})
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
if (promises == undefined || !promises[Symbol.iterator]) {
const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}`
return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`))
}
if (promises.length === 0) return
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(value => {
return resolve(value)
}, error => {
return reject(error)
})
}
})
}
}
Promise.defer = Promise.deferred = function () {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
promise-comment
/*
Promise 實現規範 Promise/A+ 地址: https://promisesaplus.com/
優點:解決異步問題
1. 多個併發異步請求,同時獲取結果 -> promise.all
2. 鏈式異步請求(惡魔金字塔、回調地獄)-> promise.then 鏈式調用
缺點:
1. 本身還是基於回調函數的形式
2. 無法中斷異步處理結果
promise循環引用問題:
循環引用只能發生在異步情況下(then 的參數函數中 或 定時器中)。此時構造函數才能執行完畢獲取到當前promise,然後再引用,發生循環引用
返回一個新的promise都會執行構造函數(調用then的時候)
promise遞歸解析問題:
解析promise即 調用 該promise的 then方法,將外層promise的resolve reject 作爲內層promise返回後 觸發執行的 then方法的 回調
then的參數函數返回promise會遞歸解析,直到返回非promise或被reject。
底層原理是將then返回的 promise的 resolve和reject 作爲參數函數返回的 promise的 then的 回調,
當參數函數返回的 promise返回後 才觸發執行
構造函數中提供的resolve方法會遞歸解析,直到返回非promise或被reject(reject不會解析promise)
底層原理是將自己和reject作爲參數promise的then的回調,當參數promise返回後,才觸發執行
因此遞歸解析的時候
某內層失敗後,外層依次調用其reject方法,也都返回失敗
最內層成功後,外層依次調用其resolve方法,也都返回成功
註冊then回調
遞推的時候不會將then的回調註冊到微任務隊列尾部
迴歸的時候,promise狀態改變纔會註冊到微任務隊列尾部,在下次循環執行
promise 異常問題:
1. 如果沒有傳遞executor函數,直接拋出異常,外面可以同步捕獲
2. 如果在executor函數體中異步代碼拋出異常,外面無法同步捕獲,只能全局捕獲(或者異步代碼自己捕獲,調用reject通知外面)
3. 其他情況下promise不會將異常拋到全局,都是返回一個失敗的promise
4. 如果在executor函數體中同步代碼拋出異常
4.1 在resolve或reject之前拋出的異常,被try-catch捕獲,返回失敗的promise
4.2 在resolve或reject接收的參數函數中拋出異常,被try-catch捕獲,返回失敗的promise
4.3 在resolve或reject之後拋出的異常,被try-catch捕獲,不影響promise的狀態
5. 如果在then回調函數中拋出異常
5.1 被then中的try-catch捕獲,返回失敗的promise
6. thenable對象
6.1 如果在其then參數函數resolve和reject之前拋異常,都會被try-catch捕獲,返回失敗的promise
e.g. Promise.resolve、構造函數中的resolve、then的resolvePromise
6.2 如果在其then參數函數resolve和reject之後拋異常,會被try-catch捕獲,但是不改變promise的狀態
promise then事件循環問題:
調用then就會將then的參數函數註冊到微任務隊列末尾,在下一輪事件循環纔會執行(延遲一輪執行)
Promise/A+ 測試問題
1. 注掉規範方法中的日誌
2. 注掉非規範中的功能(3個地方)
*/
const u = require("../utils")
const log = u.debugGenerator(__filename)
// 狀態(用常量表示)
// 1. Promise有三個狀態,resolved(fulfilled) rejected pending(默認初始狀態)
// 2. 一旦改變無法修改,只有pending狀態下才可以修改
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
// 這個方法要兼容 所有 其他庫實現的promise,例如 bluebird、q、es6-promise。這些庫可以相互調用主要靠 resolvePromise 方法兼容
const resolvePromise = (promise2, x, resolve, reject) => {
// 循環引用-自己等待自己(promise2 和 x 引用同一個對象)
if (promise2 === x) {
log.debug('promise.then circular reference')
// ES6 規範寫法 無法通過Promise/A+測試
return reject('[TypeError: Chaining cycle detected for promise #<Promise>]')
// Promise/A+ 規範
// return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
let called // 標記,防止別的庫實現的promise走成功後又走失敗
// 嚴格根據規範判斷,確保可以兼容其他庫實現的promise
// if (x instanceof Promise) { } // 不能用這種方式判斷x是不是Promise,因爲x可能是別的庫實現的Promise的實例
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
// 如果x是對象或函數
try {
// promise(x)都有一個then方法,取x的屬性then,看是不是函數來判斷x是不是promise
// 通過 x.then 取值可能會報錯,需要try-catch (參考示例 promise-resolvePromise.js)
const then = x.then
if (typeof then === 'function') {
// 至此,認爲x是promise
// 不能寫成 x.then,因爲這樣會再次取值,有可能報錯 (參考示例 promise-resolvePromise.js)
// 用 call 方法,保證then方法中的THIS是需要獲取結果的promise實例(x)。如果不call則是window或global
// 如果是thenable對象,then 方法體中可能會報錯,會被catch捕獲到
// 根據內層promise(x)的狀態和值 決定外層promise2的狀態和值
// then.call(x,
// y => {
// //【這裏調用別人實現的promise中的then方法,執行自己傳入的回調】
// // 無法控制別人的代碼執行幾個回調,只能控制自己傳入的回調(添加判斷)防止走成功後又走失敗
// if (called) return
// called = true
// // 等 x(promise) 返回成功(值爲y)。則執行x的then方法的第一個參數函數(這裏傳入的回調)
// // 即執行當前then方法返回promise2的resolve方法,使當前then返回一個成功的promise2,值爲x(promise)的成功結果y
// // resolve(y) 但是爲了解決返回promise(x)成功又返回promise的現象(y還是一個promise),這裏需要遞歸解析
// log.debug(`before resolvePromise recursion, y is '${y}'`)
// // 第一個參數仍然是最外層then返回的promise2(用來保證不發生循環引用),resolve、reject 也是promise2的
// // 當y(promise)返回後,調用promise2的resolve或reject
// // 當最終y不是promise,在【出口1或2】結束,或y返回失敗,迴歸到這裏,嵌套的resolvePromise依次結束
// resolvePromise(promise2, y, resolve, reject)
// log.debug(`end resolvePromise recursion, y is '${y}'`)
// },
// e => {
// // 防止走成功後又走失敗
// if (called) return
// called = true
// // 同理,如果 x(promise) 返回失敗,則當前then返回的promise2返回失敗,值爲x(promise)的失敗原因
// // promise(x)失敗又返回promise(即e是一個promise),不再遞歸解析,直接將最後的promise作爲失敗原因返回
// reject(e)
// })
// 根據測試結果(4.promise-then.js 最後一個測試用例),需要異步執行thenable的then方法。使用 process.nextTick(個人理解)
// 1. process.nextTick 事件將在當前階段的尾部執行(下次事件循環之前)
// 2. process.nextTick 將事件維護在 nextTickQueue 中
// 對於promise來說,沒加之前,立即調用then將回調放入 nextTickQueue 中;加了之後,先將對then的調用放入 nextTickQueue 中
// 執行會後,再將回調放入 nextTickQueue 中。即對於nextTickQueue來說,回調會延遲執行,但最終都在當前階段執行,
// 對事件循環整體來說沒有太大的影響
// 3. 無法通過Promise/A+ 測試!!!
process.nextTick(() => {
then.call(x,
y => {
//【這裏調用別人實現的promise中的then方法,執行自己傳入的回調】
// 無法控制別人的代碼執行幾個回調,只能控制自己傳入的回調(添加判斷)防止走成功後又走失敗
if (called) return
called = true
// 等 x(promise) 返回成功(值爲y)。則執行x的then方法的第一個參數函數(這裏傳入的回調)
// 即執行當前then方法返回promise2的resolve方法,使當前then返回一個成功的promise2,值爲x(promise)的成功結果y
// resolve(y) 但是爲了解決返回promise(x)成功又返回promise的現象(y還是一個promise),這裏需要遞歸解析
log.debug(`before resolvePromise recursion, y is '${y}'`)
// 第一個參數仍然是最外層then返回的promise2(用來保證不發生循環引用),resolve、reject 也是promise2的
// 當y(promise)返回後,調用promise2的resolve或reject
// 當最終y不是promise,在【出口1或2】結束,或y返回失敗,迴歸到這裏,嵌套的resolvePromise依次結束
resolvePromise(promise2, y, resolve, reject)
log.debug(`end resolvePromise recursion, y is '${y}'`)
},
e => {
// 防止走成功後又走失敗
if (called) return
called = true
// 同理,如果 x(promise) 返回失敗,則當前then返回的promise2返回失敗,值爲x(promise)的失敗原因
// promise(x)失敗又返回promise(即e是一個promise),不再遞歸解析,直接將最後的promise作爲失敗原因返回
reject(e)
})
})
} else {
// x 不是 promise(是個普通對象或普通函數),例如:{then:123}
// 遞歸出口1【終結者1】
log.debug(`the property 'then' of 'x' is not a function, x is '${x}'`)
resolve(x)
}
} catch (error) {
// x.then 取值出錯
// thenable 方法體中,執行了傳入的 resolve 參數函數後,再拋異常,也會進入這裏
log.error(`thenable error '${error}'`)
// 防止走成功後又走失敗
if (called) return
called = true
reject(error)
}
} else {
// 如果x不是對象或函數,直接返回成功狀態的promise2
// 遞歸出口2【終結者2】
log.debug(`x is not a promise, x is ${x}`)
resolve(x)
}
}
log.debug('====== my promise ======')
class Promise {
// 1. 創建類的實例,需要等構造函數中的代碼全部執行完畢,才能拿到值
// 1.1 如果resolve或reject是異步調用,則構造函數執行完畢返回 PENDING 狀態的promise
// 2. THIS
// 2.1 構造函數中的THIS指代當前實例對象
constructor(executor) {
// executor 執行器
// 1. 構造函數必須傳入一個參數,類型是函數
// 2. 如果不是函數,則直接拋類型錯誤
if (typeof executor !== "function") {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
this.status = PENDING // 狀態:初始狀態爲 pending
this.value = undefined // 值: 保存成功的結果或失敗的原因
this.onResolvedCallbacks = [] // 存放狀態變爲成功時的回調
this.onRejectedCallbacks = [] // 存放狀態變爲失敗時的回調
// 1. 調用 resolve 方法
// 1.1 如果value是promise,則異步(調用)執行,作爲 promise 成功後then的成功回調執行
// 1.2 如果value非promise,則同步(調用)執行,將value賦值給THIS(如果放到定時器中,屬於異步調用,但是調用後是立即同步執行的)
// 2. THIS
// 2.1 調用 resolve 方法的時候沒有指明誰調用的,因此這裏的THIS需要明確指向當前實例(使用箭頭函數,THIS是構造函數中的THIS)
const resolve = (value) => {
// resolve中使用模板字符串,無法通過Promise/A+測試
log.debug(`call resolve, status is '${this.status}', value is '${value}'`)
// 異步resolve('this'),會導致循環引用-自己等待自己
if (value === this) {
// 返回一個失敗的promise
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 這裏不用兼容其他版本的promise,只是自己的功能(非規範中的)
if (value instanceof Promise) {
// 遞歸解析promise,直到value非promise
// 是異步執行(涉及到then)
// 調用內部then方法,不會拋出異常
return value.then(resolve, reject)
}
// resolve解析thenable對象是ES6的功能,無法通過Promise/A+測試
if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
typeof value.then === 'function') {
// thenable 對象
// 調用內部then方法,其回調是異步執行的,而調用thenable對象中then方法,其回調是同步的(調用thenable.then就會執行)
// 因此這裏需要在調用的時候異步(微任務)
// 調用內部的then方法,無法做手腳。而thenable對象中可以對then方法做手腳,因此這裏要放到try-catch中
return process.nextTick(() => {
try {
value.then(resolve, reject)
} catch (error) {
reject(error)
}
})
}
// 只有 pending 狀態可以修改狀態和值(確保resolve和reject只會執行一次)
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(cb => cb())
}
// JSON.stringify() 丟失註冊的函數
log.debug("------promise is------", this) // 打印resolve所屬的promise
}
// 1. 調用 reject方法,同步執行(如果放到定時器中,屬於異步調用,但是調用後是立即同步執行的)
// 1.1 不管reason屬於什麼類型值,都原樣賦值給THIS
// 1.2 如果reason是promise,reject不會進行解析,直接賦值給THIS
const reject = (reason) => {
log.debug(`call reject, status is '${this.status}', reason is '${reason}'`)
// 只有 pending 狀態可以修改狀態和值(確保resolve和reject只會執行一次)
if (this.status === PENDING) {
this.value = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach(cb => cb())
}
}
// 1. executor函數,立即同步執行
// 1.1 同步代碼報錯,可以捕獲到異常
// 1.2 異步代碼報錯,無法捕獲(當異步代碼執行的時候,捕獲異常的函數已經出棧了)
// 2. executor 中默認提供 resolve reject 方法
// 2.1 調用 resolve 將狀態變爲 resolved,值爲成功結果。觸發then的成功回調執行
// 2.2 調用 reject 將狀態變爲 rejected,值爲失敗原因。觸發then的失敗回調執行
// 2.3 不是靜態方法,不是實例方法,也不是私有方法,就是一個在構造函數中定義的方法
// 2.4 是一個閉包函數,在構造函數中定義,在創建promise的地方執行
// 2.5 調用 resolve或reject 不會結束executor函數的執行,即後面的代碼依然會執行。
// 一般認爲,調用 resolve或reject後,promise的作用就完成了,後續操作應該放到then方法中,
// 因此一般在調用 resolve或reject前加上return
// 2.6 resolve 和 reject 的參數只能是值類型,如果是個表達式(new構造函數 或 普通函數[調用]),
// 會先將其在executor函數體中執行,得到表達式的返回值再傳給 resolve 或 reject 執行
// 如果在執行過程中報錯,可以被executor的try-catch捕獲
// 3. 自定義
// 3.1 成功還是失敗(什麼情況下調用 resolve/reject)由用戶決定
// 3.2 成功的結果和失敗的原因,由用戶決定
try {
executor(resolve, reject)
} catch (error) {
log.error(`catch executor error: '${error}'`)
reject(error)
}
}
// then
// 1. Promise 必須具有then方法
// 1.1 then方法需要用戶傳入兩個參數函數(回調函數),第一個是狀態變爲成功時觸發執行(接收成功的結果)【成功回調】,
// 第二個是狀態變爲失敗時觸發執行(接收失敗的原因)【失敗回調】。【兩個參數函數只能觸發執行一個】
// 1.2 如果某個參數函數沒有傳遞,則會使用默認參數函數
// 1.3 then方法同步執行,但是傳入的兩個參數函數(回調)是異步執行
// ES6的Promise中then屬於微任務,其他Promise庫可能是宏任務(bluebird)
// 無法自己實現一個微任務,只能調用宿主環境提供的API
// 1.4 then方法在調用參數函數時會傳入'THIS'(調用then的promise實例)的值,即參數函數可以拿到當前promise的值
// 2. then方法 返回一個【新】的promise
// 2.1 then 方法的執行過程類似執行構造函數,處理完回調函數(註冊到微任務隊列或添加到待執行隊列)之後,立即返回 PENDING 狀態的promise
// 繼續執行後續同步代碼(因此鏈式調用會同步執行then方法,完後再執行then方法的回調)
// 3. then方法 返回promise的狀態 及 鏈式調用 promise返回值的傳遞規則:
// 3.1 需要在參數函數中用return明確指定返回值,否則then方法默認返回一個成功的promise,值是undefined,傳入下一個then的成功回調中
// 3.2 如果參數函數返回的是【普通值】(非promise實例、thenable對象、異常,即普通對象、數字、字符串、undefined(默認))
// 則then方法返回一個成功的promise,值是該普通值,傳入下一個then的成功回調中
// 3.3 如果參數函數【拋出異常】,會被then內部的try-catch捕獲
// 則then方法返回一個失敗的promise,值是異常原因,傳入下一個then的失敗回調中
// 3.4 如果參數函數返回一個【promise實例】,則該promise實例的狀態會決定當前then方法返回promise的狀態,從而決定下一個then參數函數的執行情況
// 3.4.1 如果參數函數返回一個成功的promise,則當前then也返回一個成功的promise,值是參數函數返回promise的成功結果,傳入下一個then的成功回調中
// 3.4.2 如果參數函數返回一個失敗的promise,則當前then也返回一個失敗的promise,值是參數函數返回promise的失敗原因,傳入下一個then的失敗回調中
// 4. 錯誤處理
// 4.1 如果距離自己最近的then沒有傳遞第二個參數函數,則找下一個then或catch
// 5. THIS
// 5.1 then方法中的THIS是調用then的promise實例
then(onResolved, onRejected) {
// 方法中的THIS是調用then的promise實例
log.info(`call then, promise status is ${this.status}`)
// 判斷是否傳遞參數以及傳遞的是不是函數
// onResolved = typeof onResolved === 'function' ? onResolved : value => { return value }
onResolved = typeof onResolved === 'function' ? onResolved : v => v
// onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e }
// 懶遞歸,每次調用就new一個新的promise
const promise2 = new Promise((resolve, reject) => {
// then方法同步執行(判斷是同步的),回調函數異步執行
if (this.status === RESOLVED) {
// then中回調函數異步執行,可以用 setTimeout 或 process.nextTick 模擬實現【只能用一種,不能混用】
// ES6 規範中 then 是微任務,這裏無法自己實現一個微任務,只能調用宿主環境提供的API(process.nextTick)
// then方法同步執行到這裏,創建匿名函數的時候,promise2 還沒有定義(等構造函數中的代碼全部執行完畢,才能拿到promise2)
// 構造函數還沒有執行完,但是在構造函數中就使用了實例,因此匿名函數的執行一定是異步的,才能在執行時拿到實例
// setTimeout(() => {
// try {
// const x = onResolved(this.value)
// log.debug("RESOLVED:then return x =", x)
// resolvePromise(promise2, x, resolve, reject)
// } catch (error) {
// reject(error)
// }
// }, 0)
process.nextTick(() => {
try {
// 回調函數異步執行,外面executor的try-catch無法捕獲到異常,因此需要在源頭捕獲
const x = onResolved(this.value)
// 如果x是普通值,可以直接 resolve(x)
log.debug("RESOLVED:then return x =", x)
// 遞歸解析回調函數的返回值x,決定then返回的promise2的狀態
// 如果x是promise,調用該promise的then方法時,傳遞的兩個參數函數就是當前then返回promise2的executor中提供的resolve reject
// 1. 如果該promise返回成功,則調用當前then返回promise2的resolve方法,使當前then返回一個成功的promise2
// 2. 如果該promise返回失敗,則調用當前then返回promise2的reject方法,使當前then返回一個失敗的promise2
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
// 參數函數異常,then返回一個失敗的promise2
log.error("RESOLVED: catch error:", error)
reject(error)
}
})
}
if (this.status === REJECTED) {
// setTimeout(() => {
// try {
// const x = onRejected(this.value)
// log.debug("REJECTED:then return x =", x)
// resolvePromise(promise2, x, resolve, reject)
// } catch (error) {
// reject(error)
// }
// }, 0)
process.nextTick(() => {
try {
const x = onRejected(this.value)
// 如果x是普通值,可以直接 resolve(x)
log.debug("REJECTED:then return x =", x)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
log.error("REJECTED: catch error:", error)
reject(error)
}
})
}
// 如果 executor 裏面異步調用resolve或reject,則調用then方法時,當前promise是pending狀態
// 如果當前狀態是 pending,需要用發佈訂閱模式,則將傳入的回調函數保存起來,稍後執行resolve或reject改變狀態時再觸發執行
// 同一個promise可以多次調用 then 方法,因此會有多個回調函數,需要用數組保存
if (this.status === PENDING) {
// AOP
this.onResolvedCallbacks.push(() => {
// 不是立即執行,當執行外面的匿名函數的時候,纔會執行
// do other things...
// setTimeout(() => {
// try {
// const x = onResolved(this.value)
// log.debug("PENDING->RESOLVED:then return x =", x)
// resolvePromise(promise2, x, resolve, reject)
// } catch (error) {
// reject(error)
// }
// }, 0)
process.nextTick(() => {
try {
const x = onResolved(this.value)
// 如果x是普通值,可以直接 resolve(x)
log.debug("PENDING->RESOLVED:then return x =", x)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
log.error("PENDING->RESOLVED: catch error:", error)
reject(error)
}
})
})
this.onRejectedCallbacks.push(() => {
// setTimeout(() => {
// try {
// const x = onRejected(this.value)
// log.debug("PENDING->REJECTED:then return x =", x)
// resolvePromise(promise2, x, resolve, reject)
// } catch (error) {
// reject(error)
// }
// }, 0)
process.nextTick(() => {
try {
const x = onRejected(this.value)
// 如果x是普通值,可以直接 resolve(x)
log.debug("PENDING->REJECTED:then return x =", x)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
log.error("PENDING->REJECTED: catch error:", error)
reject(error)
}
})
})
}
})
return promise2
}
//=============================================以下非Promise/A+ 規範===============================================
// 返回一個新的promise,根據 onRejected 的返回結果決定返回promise的狀態
catch(onRejected) {
// THIS指代調用catch的promise實例
return this.then(null, onRejected)
}
// node>10
// 表示前面的promise無論成功還是失敗都會執行finally方法
// 當無論如何必須要處理一個邏輯的時候使用,如果返回成功的promise不影響整個then鏈的結果
// callback
// 1. 調用callback不會傳遞參數(無法拿到前面promise的返回值)
// 2. callback最終在then的參數函數中被調用
// 3. callback返回一個promise(如果不是則用Promise.resolve轉換爲promise),且會等待這個promise返回
// finally值傳遞規則
// 調用then方法返回一個promise,根據callback的執行結果決定自己的狀態和值
// 1. 如果callback返回的promise成功,則finally返回成功的promise,值爲前面promise的成功結果,傳遞下去(遵循 then 的鏈式調用原理)
// 2. 如果callback返回的promise失敗,則finally返回失敗的promise,值爲callback返回promise的失敗原因,取代並傳遞下去(遵循 then 的鏈式調用原理)
// 3. 如果callback執行報錯,則被當前then回調的try-catch捕獲,finally返回失敗的promise,值爲報錯原因,取代並傳遞下去
finally(callback) {
log.info(`call finally, promise is ${JSON.stringify(this)}`)
return this.then(value => {
log.debug("finally: previous promise is resolved")
// 如果前面promise成功,則進入這裏
// 執行順序:在回調函數中:
// 1.執行 callback(),返回一個值
// 2.執行 Promise.resolve(),返回一個promise
// 3.執行 then方法,處理回調 '()=>value'
// 4.返回一個 PENDING 狀態的promise。(此時對外面的then方法來說就是第一個參數回調返回值x是一個promise,繼續解析)
return Promise.resolve(callback()).then(() => value)
}, err => {
log.debug("finally: previous promise is rejected")
// 如果前面的promise失敗,則進入這裏
return Promise.resolve(callback()).then(() => { throw err })
})
}
// 將當前值轉換爲promise對象:Promise.resolve([value])
// 參數:
// 1. 是一個promise實例,則直接原樣返回
// 2. 是一個thenable對象,則異步調用其then方法,決定resolve返回promise的狀態
// 2.1 Promise.resolve([thenable]) 可能會返回一個失敗的promise
// 3. 不是thenable對象或promise實例,則返回一個新的成功的promise,值爲該參數
// 4. 不傳參數,返回一個新的成功的promise,值爲undefined
static resolve(value) {
// 不處理兼容
if (value instanceof Promise) {
// 原樣返回
return value
}
return new Promise((resolve, reject) => {
if (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
typeof value.then === 'function') {
// thenable 對象
// 調用內部then方法,其回調是異步執行的,而調用thenable對象中then方法,其回調是同步的(調用thenable.then就會執行)
// 因此這裏需要在調用的時候異步(微任務)
// 調用內部的then方法,無法做手腳。而thenable對象中可以對then方法做手腳,因此這裏要放到try-catch中
process.nextTick(() => {
try {
value.then(resolve, reject)
} catch (error) {
reject(error)
}
})
} else {
return resolve(value)
}
})
}
// 將當前值轉換爲一個失敗的promise對象:Promise.reject([value])
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// 參數:實現iterator接口的可迭代對象(數組、字符串)
// 1. 如果參數不存在或者不可迭代,返回一個失敗的promise,值爲類型錯誤
// 2. 如果可迭代對象成員爲空,返回一個成功的promise,值爲空數組
// 3. 如果可迭代對象成員不是promise,則調用 Promise.resolve 將其變爲一個promise
// 返回promise的狀態:由所有可迭代對象的成員(promise)的返回狀態決定
// 1. 所有成員promise都返回成功,則all返回一個成功的promise,值爲所有成員promise返回值組成的數組(按成員順序排序)
// 2. 只要一個成員promise返回失敗,則all返回一個失敗的promise,值爲第一個失敗的成員promise的失敗原因
// 3. 如果成員promise自身定義了catch方法,那麼它被rejected時會被自身定義的catch捕獲,
// 並返回一個新的promise(用這個新promise狀態代替該成員promise狀態)
static all(promises) {
return new Promise((resolve, reject) => {
if (promises == undefined || !promises[Symbol.iterator]) {
const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}`
return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`))
}
if (promises.length === 0) return resolve([])
let index = 0
const resultArr = []
function processValue(i, value) {
resultArr[i] = value
if (++index === promises.length) {
resolve(resultArr)
}
}
for (let i = 0; i < promises.length; i++) {
//promises[i] 可能是普通值,用 Promise.resolve 包一層,確保都是promise
Promise.resolve(promises[i]).then((value) => {
processValue(i, value)
}, (err) => {
// 有一個失敗則結束循環
return reject(err)
})
}
})
}
// 參數:實現iterator接口的可迭代對象(數組、字符串)
// 1. 如果參數不存在或者不可迭代,返回一個失敗的promise,值爲類型錯誤
// 2. 如果可迭代對象成員爲空,【返回一個PENDING 狀態的promise】
// 3. 如果可迭代對象成員不是promise,則調用 Promise.resolve 將其變爲一個promise
// 返回promise的狀態:
// 1. 只要一個成員promise返回,則race返回相同狀態的promise
static race(promises) {
return new Promise((resolve, reject) => {
if (promises == undefined || !promises[Symbol.iterator]) {
const preReason = promises === undefined ? `${promises}` : `${typeof promises} ${promises}`
return reject(new TypeError(`${preReason} is not iterable (cannot read property Symbol(Symbol.iterator))`))
}
if (promises.length === 0) return
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then((value) => {
return resolve(value)
}, (err) => {
return reject(err)
});
}
});
}
}
// 測試入口
// Promise的延遲對象,測試的時候會調用這個函數
Promise.defer = Promise.deferred = function () {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
測試
更詳細的測試用例參考repo https://github.com/lfp1024/promise
resolve 解析 promise
- resolve(promise) 可以遞歸解析,由內層promise的狀態決定外層promise的狀態
- reject(promise) 不會遞歸解析,外層promise直接返回失敗,失敗原因是promise
var p1 = new Promise(function (resolve, reject) {
resolve(new Promise(res => res('resolve')));
});
var p2 = new Promise(function (resolve, reject) {
// 將resolve實參promise的狀態變爲自己的狀態,將實參promise的值變爲自己的值
resolve(new Promise((res, rej) => { rej('reject') }));
});
var p3 = new Promise(function (resolve, reject) {
reject(new Promise(res => res('resolve')));
});
p1.then(
function fulfilled(value) { console.log('p1 fulfilled: ' + value); },
function rejected(err) { console.log('p1 rejected: ' + err); }
);
p2.then(
function fulfilled(value) { console.log('p2 fulfilled: ' + value); },
function rejected(err) { console.log('p2 rejected: ' + err); }
);
p3.then(
function fulfilled(value) { console.log('p3 fulfilled: ' + value); },
function rejected(err) { console.log('p3 rejected: ' + err); }
);
// 輸出結果:
// p3 rejected: [object Object]
// p1 fulfilled: resolve
// p2 rejected: reject
/*
解釋:
// 同步代碼執行
p1 的 resolve 註冊到下一輪循環執行
p2 的 resolve 註冊到下一輪循環執行
p3 的 reject 當前執行棧中同步執行,將 p3.then 註冊到下一輪循環執行
// 下一輪循環
p1 的 resolve 執行,將 p1.then 註冊到下一輪循環執行
p2 的 resolve 執行,將 p2.then 註冊到下一輪循環執行
p3.then 執行,輸出 => p3 rejected: [object Object]
// 下一輪循環
p1.then 執行,輸出 => p1 fulfilled: resolve
p2.then 執行,輸出 => p2 rejected: reject
*/
resolve 解析 thenable對象
- ES6 的resolve可以解析thenable對象。且thenable.then屬於微任務
- thenable.then方法中的參數函數的執行情況會決定當前promise的狀態
console.log("--script start--")
let obj = {
then: function (onResolved, onRejected) {
console.log("異步執行thenable then")
onResolved('成功啦')
}
}
setTimeout(() => {
console.log("setTimeout")
}, 0);
let p1 = new Promise(function (resolve, reject) {
console.log("1")
resolve(obj);
console.log("2")
});
p1.then(res=>{
console.log("promise then")
})
console.log("p1 =", p1)
setTimeout(() => {
console.log("p1 = ", p1)
}, 0);
console.log("--script end--")
// 從輸出順序可以看出調用thenable的then方法屬於微任務
// --script start--
// 1
// 2
// p1 = Promise { <pending> }
// --script end--
// 異步執行thenable then
// promise then
// setTimeout
// p1 = Promise { '成功啦' }
// 如果是宏任務,則輸出順序應爲(調用resolve之前,p1.then尚未註冊到事件隊列中)
// --script start--
// 1
// 2
// p1 = Promise { <pending> }
// --script end--
// setTimeout
// 異步執行thenable then
// promise then
// p1 = Promise { '成功啦' }
then 遞歸解析 promise
- then 會遞歸解析其回調函數返回的promise。最內層promise的狀態決定外層then返回promise的狀態
let p = new Promise((resolve, reject) => {
resolve(1)
})
// [resolve(promise)]-------------------------------------------------
// 3層promise:最外層p1,中間層p2(then的第一個回調返回的promise),最內層p3(1秒後成功或失敗)
let p1 = p.then(value => {
// then的參數函數返回一個 new Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve 又返回一個promise,遞歸解析
resolve(new Promise((resolve, reject) => {
setTimeout(() => {
// 最內層promise調用resolve成功之後,會執行其then的第一個回調,即執行中間層p2的resolve
// resolve("success")
// 最內層promise調用reject失敗之後,會執行其then的第二個回調,即執行中間層p2的reject
reject("fail")
}, 1000);
}))
}, 1000);
})
})
// 外面想要獲取當前then返回promise的結果,還是要調用當前then返回promise的then方法
p1.then(res => {
console.log("當前then返回promise的結果1", res) // 當前then返回promise的結果1 success
}, err => {
console.log("當前then返回promise的原因1", err) // 當前then返回promise的原因1 fail
})
// [reject(promise)]-------------------------------------------------
let promise = new Promise((resolve, reject) => {
resolve(1)
})
let promise2 = promise.then(value => {
// then的參數函數返回一個 new Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// reject 又返回的一個promise,不再遞歸解析(但是會執行new表達式,執行完後立即返回一個 PENDING 狀態的promise)
reject(new Promise((resolve, reject) => {
console.log("執行")
setTimeout(() => {
resolve("success")
}, 1000);
}))
}, 1000);
})
})
// 外面想要獲取當前then返回promise的結果,還是要調用當前then返回promise的then方法
promise2.then(res => {
console.log("當前then返回promise的結果2", res)
}, err => {
console.log("當前then返回promise的原因2", err) // 當前then返回promise的原因2 Promise { <pending> }
})
then 解析 thenable 對象
- then解析 thenable 異步調用其then 方法
console.log("--script start--")
let obj = {
then: function (onResolved, onRejected) {
console.log("異步執行thenable then")
onResolved('成功啦')
// onRejected('失敗啦')
// throw Error("Oops!")
}
}
setTimeout(() => {
console.log("setTimeout")
}, 0)
let p = Promise.resolve('ok')
let p1 = p.then(res => {
console.log("p2 then")
return obj
})
let p2 = Promise.resolve('ok2')
let p3 = p2.then(res => {
console.log("p3 then")
return
})
console.log("p1 =", p1)
setTimeout(() => {
console.log("p1 = ", p1)
}, 0)
console.log("--script end--")
// 輸出:
// --script start--
// p1 = Promise { <pending> }
// --script end--
// p2 then
// p3 then
// 異步執行thenable then
// setTimeout
// p1 = Promise { '成功啦' }
// 如果不是異步調用,順序是:
// --script start--
// p1 = Promise { <pending> }
// --script end--
// p2 then
// 異步執行thenable then
// p3 then
// setTimeout
// p1 = Promise { '成功啦' }
then 宏任務實現和微任務實現
let fs = require('fs')
// 需求是讀取一個文件獲取路徑,然後再繼續讀取內容
// 當前路徑下有兩個文件 name.txt 和 age.txt。name.txt 內容是 age.txt。age.txt 內容是 27。
// 先讀取 name.txt 拿到 age.txt,再讀取 age.txt 內容
fs.readFile('./name.txt', 'utf-8', (err, data) => {
if (err) {
// 錯誤處理...
}
console.log("data = ", data)
fs.readFile(data, 'utf-8', (err, data) => {
if (err) {
// 錯誤處理...
}
console.log("data = ", data)
})
})
function readFileSync(filePath) {
return new Promise((res, rej) => {
fs.readFile(filePath, 'utf-8', (err, data) => {
if (err) return rej(err)
return res(data)
})
})
}
readFileSync('./name.txt').then((data) => {
console.log('獲取到數據', data)
return readFileSync(data)
}).then((data) => {
console.log('獲取到數據', data)
}, (err) => {
console.log('失敗', err)
})
/*
用 setTimeout 實現then的異步調用(宏任務),跟es6執行順序不同
輸出結果:
data = age.txt
data = 27
獲取到數據 age.txt
獲取到數據 27
分析:
// 第一輪
fs.readFile 添加一個宏任務1(讀取到數據之後觸發)
readFileSync 添加一個宏任務2(讀取到數據之後觸發)
// 第二輪(讀取到數據)
宏任務1執行,輸出=>data = age.txt,添加宏任務3
宏任務2執行,添加宏任務4(then)
// 第三輪
宏任務3執行,輸出=>data = 27
// 第四輪
宏任務4執行,輸出=>獲取到數據 age.txt,添加宏任務5
// 第五輪
宏任務5執行,添加宏任務6(then)
// 第六輪
宏任務6執行,輸出=>獲取到數據 27
*/
/*
換用 process.nextTick 來實現則執行順序相同
輸出結果:
data = age.txt
獲取到數據 age.txt
data = 27
獲取到數據 27
分析:
// 第一輪
fs.readFile 添加一個宏任務1(讀取到數據之後觸發)
readFileSync 添加一個宏任務2(讀取到數據之後觸發)
// 第二輪(讀取到數據)
宏任務1執行,輸出=>data = age.txt,添加宏任務3
宏任務2執行,添加微任務1(then)
// 第三輪
微任務1執行,輸出=>獲取到數據 age.txt,添加宏任務4
// 第四輪
宏任務3執行,輸出=>data = 27
// 第五輪
宏任務4執行,添加微任務2(then)
// 第六輪
微任務2執行,輸出=>獲取到數據 27
*/
Promise.resolve 解析 promise
- Promise.resolve 解析promise
- Promise.reject 不解析promise
var p1 = new Promise(function (resolve, reject) {
resolve(Promise.resolve('resolve'));
});
var p2 = new Promise(function (resolve, reject) {
resolve(Promise.reject('reject'));
});
var p3 = new Promise(function (resolve, reject) {
reject(Promise.resolve('resolve'));
});
p1.then(
function fulfilled(value) { console.log('p1 fulfilled: ' + value); },
function rejected(err) { console.log('p1 rejected: ' + err); }
);
p2.then(
function fulfilled(value) { console.log('p2 fulfilled: ' + value); },
function rejected(err) { console.log('p2 rejected: ' + err); }
);
p3.then(
function fulfilled(value) { console.log('p3 fulfilled: ' + value); },
function rejected(err) { console.log('p3 rejected: ' + err); }
);
// p3 rejected: [object Promise]
// p1 fulfilled: resolve
// p2 rejected: reject
參考
珠峯姜老師公開課
https://promisesaplus.com/
https://github.com/YvetteLau/Blog/issues/2