promise

源碼

  • 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)
    }
  })
}
複製代碼
相關文章
相關標籤/搜索