源碼
- Promise的三種狀態常量 —— pending:等待態; resolved:成功態; rejected:失敗態
- 鏈式調用的本質 —— promise的then方法必須返回一個新的promise對象
鏈式調用的本質:
promise2 = promise1.then(onFulfilled, onRejected)
promise1.then(onFulfilled1, onRejected1).then(onFulfilled2, onRejected2)
複製代碼
源碼(promise.js):
function Promise (executor) { // 構造函數Promise必須接受一個函數(executor)做爲參數
this.status = 'pending' // 初始狀態爲pending
this.value = undefined // 成功的值
this.reason = undefined // 失敗的緣由
this.onResolvedCallbacks = [] // 保存成功回調函數隊列(new Promise()時可能會有異步操做),見[案例4]
this.onRejectedCallbacks = [] // 保存失敗回調函數隊列(new Promise()時可能會有異步操做)
resolve = value => { // resovle時的執行函數(把狀態變成resolved)
if (this.status === 'pending') { // 只有等待態 能夠改變狀態
this.value = value
this.status = 'resolved'
this.onResolvedCallbacks.forEach(fn => fn()) // 異步執行resolve()時,作【發佈】操做,見[案例4]
}
}
reject = reason => { // reject時的執行函數(把狀態變成rejected)
if (this.status === 'pending') { // 只有等待態 能夠改變狀態
this.reason = reason
this.status = 'rejected'
this.onRejectedCallbacks.forEach(fn => fn()) // 異步執行reject()時,作【發佈】操做
}
}
try { // new Promise()時, 直接執行executor函數(同步執行)
executor(resolve, reject) // executor函數包含resolve和reject兩個參數(且都爲函數)
} catch (err) { // 若是執行exectuor時出錯,就讓當前promise變成失敗態
reject(err)
}
}
/**
* @param {*} promise2 調用then時返回的【新的】promise對象
* @param {*} x 【當前】then中onFulfilled或onRejected函數的返回值
* 若x不爲Promise,則x直接做爲Promise2的值(即新的onFulfilled或onRejected函數的參數)
* 若x 爲 Promise,則promise2的一個回調函數(resolve或reject),會等待該Promise對象(即x)狀態變化後纔會被調用,且Promise2的狀態和x的狀態相同
* @param {*} resolve promise2的resolve
* @param {*} reject promise2的reject
*/
function resolvePromise (promise2, x, resolve, reject) {
if (promise2 === x) { // 不能本身等本身,見[案例1]
return reject(new TypeError('Chaining cycle detected for promise'))
}
let called = false // 防止屢次調用(既調成功也調失敗),別人的代碼可能沒有判斷 —— 只有等待態 能夠改變狀態
if (x !== null && (typeof x === 'object' || typeof x === 'function')) { //【1+】x是對象或函數纔有多是promise
try { // 嘗試取x的then屬性,進一步判別x是否爲promise,可能會拋異常(這個promise多是別人亂寫的),見[案例5]
let t = x.then
if (typeof t === 'function') { //【2+】x.then爲函數纔多是promise
/**
* x多是promise,直接讓x.then執行
* 一、promise.then() // this指向promise
* 二、let t = promise.then
* t() // this指向全局,經過t.call(x, ...)將this指向x
*
* 如下至關於:
* x.then(y => {}, err => {})
*/
t.call(x, y => { // promise成功的結果,見[案例7]
if (called) return // 防止屢次調用(既調成功也調失敗),別人的代碼可能沒有判斷 —— 只有等待態 能夠改變狀態
called = true
resolvePromise(promise2, y, resolve, reject) // 成功的結果多是promise,須要【遞歸】解析,直到解析成普通值爲止
}, err => { // promise失敗的結果,見[案例6]
if (called) return
called = true
reject(err)
})
} else { //【2-】x不爲promise(即爲普通值,如:x爲{then: {}})
resolve(x)
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else { //【1-】x不爲promise(即爲普通值)
resolve(x)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) { // then是異步調用,此處用setTimeout模仿 (原生的then的成功或失敗 是一個微任務)
/**
* onFulfilled和onRejected是可選參數,若是onFulfilled或onRejected不是函數,必須被忽略
* 若是onFulfilled不是函數且promise爲成功態,則promise2爲成功態並返回promise成功的值,見[案例3]
* 若是onRejected 不是函數且promise爲失敗態,則promise2爲失敗態並返回promise失敗的值
*/
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
let promise2 = new Promise((resolve, reject) => { // 每次調用then時必須返回一個新的promise對象
switch (this.status) { // this指向promise
case 'resolved':
setTimeout(() => {
try { // 若是onFulfilled或onRejected拋出異常,則promise2必須變爲失敗態,見[案例2]
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
}, 0)
break
case 'rejected':
setTimeout(() => {
try { // 若是onFulfilled或onRejected拋出異常,則promise2必須變爲失敗態
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
}, 0)
break
case 'pending':
this.onResolvedCallbacks.push(() => { // 異步執行resolve()時,作【訂閱】操做,見[案例4]
setTimeout(() => {
try { // 若是onFulfilled或onRejected拋出異常,則promise2必須變爲失敗態
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
}, 0)
})
this.onRejectedCallbacks.push(() => { // 異步執行reject()時,作【訂閱】操做
setTimeout(() => {
try { // 若是onFulfilled或onRejected拋出異常,則promise2必須變爲失敗態
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (err) {
reject(err)
}
}, 0)
})
break
}
})
return promise2
}
複製代碼
測試代碼:
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
// npm install promises-aplus-tests -g
// promises-aplus-tests promise.js
複製代碼
案例1:promise2和onFulfilled或onRejected函數返回的結果是同一個對象
let promise = new Promise((resolve, reject) => {
resolve()
})
let promise2 = promise.then(res => { // 不能本身等本身
return promise2
})
// TypeError: Chaining cycle detected for promise
複製代碼
案例2:若是onFulfilled或onRejected拋出異常,則promise2必須變爲失敗態
let promise = new Promise((resolve, reject) => {
resolve()
})
promise2 = promise.then(res => {
throw new Error('這裏拋出一個異常')
})
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) // 這裏拋出一個異常
})
複製代碼
案例3:若是onFulfilled不是函數且promise爲成功態,則promise2爲成功態並返回promise成功的值
let promise = new Promise((resolve, reject) => {
resolve('success')
})
promise2 = promise.then('這裏的onFulfilled原本是一個函數,但如今不是')
promise2.then(res => {
console.log(res) // success
}, err => {
console.log(err)
})
複製代碼
案例4:異步執行resolve()時,作【發佈、訂閱】操做
非異步:
let promise = new Promise((resolve, reject) => {
resolve('成功') // 非異步執行
})
promise.then(res => { // 狀態爲resolved,值爲成功,直接執行
console.log(res) // 成功
})
異步:
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 異步執行
}, 1000)
})
promise.then(res => { // 狀態爲pending,值爲undefined,執行【訂閱】操做;1s後狀態爲resolved,值爲成功,執行【發佈】操做
console.log(res) // 1s後,成功
})
複製代碼
案例5:嘗試取x的then屬性,進一步判別x是否爲promise時,可能會拋異常
let obj = {}
Object.defineProperty(obj, 'then', {
get () {
throw new Error('出錯')
}
})
console.log(obj.then) // 出錯
複製代碼
案例6:x.then失敗
let promise = new Promise((resolve, reject) => {
resolve()
})
let promise2 = promise.then(res => {
return new Promise((resolve, reject) => {
reject('失敗')
})
})
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) // 失敗
})
複製代碼
案例7:x.then成功
let promise = new Promise((resolve, reject) => {
resolve()
})
let promise2 = promise.then(res => {
return new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => {
resolve('成功')
}))
})
})
promise2.then(res => {
console.log(res) // 成功
}, err => {
console.log(err)
})
複製代碼
應用
let fs = require('fs')
function read (filePath, encoding) {
return new Promise((resolve,reject) => {
fs.readFile(filePath, encoding, (err, data) => {
if(err) reject(err)
resolve(data)
})
})
}
read('1.txt', 'utf8').then(res => {
return read(res, 'utf8')
}).then(res => {
return read(res, 'utf8')
}).then(res => {
console.log(res) // hello
})
// 1.txt內容 2.txt
// 2.txt內容 3.txt
// 3.txt內容 hello
複製代碼
catch(catch的實現基於then,可理解爲不傳成功的then)
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected)
}
複製代碼
resolve
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
複製代碼
reject
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
複製代碼
all
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let arr = []
let i = 0
function processData (index, data) {
arr[index] = data
if (++i == promises.length) {
resolve(arr)
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(data => { // data是成功的結果
processData(i, data)
}, reject)
}
})
}
複製代碼
race
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve,reject)
}
})
}
複製代碼