Louis's blog
GitHub
GitHub
  • 瀏覽器運作原理
  • 閉包
  • this
  • arguments 和 array-like
  • apply、call、bind
  • 函式程式設計
  • 物件和原型
  • 原型鏈和繼承
  • ES6 和 Class
  • ES6 中新增的數據類型
  • ES6 語法糖
  • var、let、const
  • ES7~ES12
  • Strict Mode
  • 補充
  • Proxy 和 Reflect
  • Promise
  • Iterator
  • 生成器
  • async、await
  • Event loop
  • 錯誤處理
  • 模塊化
  • 套件管理工具
  • JSON
  • WebStorage
  • IndexDB
  • Cookie
  • BOM
  • DOM
  • 防抖(debounce)和節流(throttle)
  • Deep copy
  • 事件總線

Promise

在沒有 Promise 是如和處理的網路請求的呢?可以通過 callback 的形式:

function requestData(params, successCallback, failtureCallback) {
  setTimeout(() => {
    if (params === 'google') {
      // 成功
      let data = ['fkfk', 'fkfk', 'fkfkf']
      successCallback(data)
    } else {
      // 失敗
      let errMessage = '請求失敗'
      failtureCallback(errMessage)
    }
  })
}

requestData(
  'google',
  (res) => {
    console.log(res)
  },
  (err) => {
    console.log(err)
  }
)

callback 方式的弊端:

  • 需要自己設計好 requestData
  • 如果使用第三方庫,需要看文檔或是源代碼才能了解如何使用

ES6 就實現了一個更好的方案,Promise。

Promise 的基本使用

  • Promise 是一個類,可以翻譯成承諾、期約。
  • 在通過 new 創建 Promise 物件時,需要傳入一個回調函數,稱之為 executor,回調函數有兩個參數 resolve 和 reject ,當調用 resolve 時,會執行 Promise 物件的 then 方法傳入的回調函數,當調用 reject 時,會執行 Promise 物件的 catch 方法傳入的回調函數。
// 成功時調用 resolve
// 失敗時調用 reject
const promise = new Promise((resolve, reject) => {
  resolve()
})

promise.then(() => {
  console.log('----success---')
})

promise.catch(() => {
  console.log('----err----')
})

非同步請求的 Promise:

function requestData(params) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (params === 'google') {
        // 成功
        let data = ['fkfk', 'fkfk', 'fkfkf']
        resolve(data)
      } else {
        // 失敗
        let errMessage = '請求失敗'
        reject(errMessage)
      }
    }, 3000)
  })
}

const promise = requestData('google')

// then 中可以同時處理 resolve 和 reject
promise.then(
  (res) => {
    console.log(res)
  },
  (err) => {
    console.log(err)
  }
)

Promise 可以被分為幾個階段,每個階段一但被確立就是不可更改的:

new Promise((resolve, reject) => {
  // pending 階段
  resolve()
}).then(
  (res) => {
    // fulfilled 階段
    console.log(res)
  },
  (err) => {
    // rejected 階段
    console.log(err)
  }
)

resolve 參數

resolve 參數有兩個例外情況。

當傳入 Promise 物件作為 resolve 參數時,那麼當前 Promise 的狀態會交由傳入的 Promise 來決定:

const newPromise = new Promise((resolve, reject) => {})

new Promise((resolve, reject) => {
  resolve(newPromise)
  // newPromise沒調用resolve, reject所以不會往下執行
}).then(
  (res) => {
    console.log(res)
  },
  (err) => {
    console.log(err)
  }
)

當傳入一個物件,並且該物件有實現 then 方法時,那麼會執行 then 方法,並且由該方法決定後續狀態:

const obj = {
  then(resolve, reject) {
    reject('error')
  }
}

new Promise((resolve, reject) => {
  resolve(obj)
}).then(
  (res) => {
    console.log(res)
  },
  (err) => {
    // 打印 error
    console.log(err)
  }
)

Promise 的實例方法

then

當 resolve 被調用時,所有 then 方法都會被調用:

const promise = new Promise((resolve, reject) => {
  resolve('1111')
})

// 當 resolve 被調用時,所有 then 方法都會被調用
promise.then((res) => {
  console.log(res)
})

promise.then((res) => {
  console.log(res)
})

promise.then((res) => {
  console.log(res)
})

當返回一個值,那麼這個值會作為一個新的 Promise 的 resolve 值:

const promise = new Promise((resolve, reject) => {
  resolve('1111')
})

// 當返回一個普通值,那麼這個值會作為一個新的 Promis 的 resolve 值
promise
  .then((res) => {
    return '123'
  })
  .then((res) => {
    console.log(res)
  })

catch

當 excutor 拋出異常時,也會調用錯誤捕獲的回調函數:

const promise = new Promise((resolve, reject) => {
  throw new Error()
  // reject('1111')
})

promise.catch((err) => {
  console.log('---')
})

catch 也可以寫在 then 方法之後,相當於一個語法糖,前面的 promise 拋出的錯誤都會進行捕獲:

const promise = new Promise((resolve, reject) => {
  throw new Error()
  // reject('1111')
})

promise
  .then((res) => {
    return new Promise((resolve, reject) => {
      reject('error')
    })
  })
  .catch((err) => {
    console.log(err)
  })

catch 的返回值也是一個 Promise:

const promise = new Promise((resolve, reject) => {
  throw new Error()
  // reject('1111')
})

promise
  .then((res) => {})
  .catch((err) => {
    return '-----'
  })
  .then((res) => {
    // 輸出:-----
    console.log(res)
  })

finally

finally 是 ES9 新增的一個方法,Promise 無論成功失敗都會執行:

const promise = new Promise((resolve, reject) => {
  resolve('success')
})

promise
  .then((res) => {
    console.log(res)
  })
  .catch((err) => {
    console.log(err)
  })
  .finally(() => {
    console.log('finally')
  })

Promise 的類方法

Promise.resolve

可以將一個值轉換為 Promise 物件,並且回調 resolve 函數:

const objPromise = Promise.resolve({ name: 'Louis' })
objPromise.then((res) => {
  console.log(res)
})

等價於:

const objPromise = new Promise((resolve, reject) => {
  resolve({ name: 'Louis' })
})
objPromise.then((res) => {
  console.log(res)
})

Promise.reject

可以將一個值轉換為 Promise 物件,並且回調 reject 函數:

const objPromise = Promise.reject({ name: 'Louis' })
objPromise.catch((err) => {
  console.log(err)
})

等價於:

const objPromise2 = new Promise((resolve, reject) => {
  reject({ name: 'Louis' })
})
objPromise.catch((err) => {
  console.log(err)
})

Promis.all

同時拿到全部 Promise 返回的結果,result 為返回一個陣列:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('1111')
  }, 500)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('2222')
  }, 200)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('333')
  }, 700)
})

// 結果是依需返回
// 只要有一個 promise 變成 reject ,那麼會直接調用 catch
Promise.all([p1, p2, p3, '---'])
  .then(([res1, res2, res3, res4]) => {
    console.log(res1, res2, res3, res4)
  })
  .catch((err) => {
    console.log(err)
  })

Promise.allSettled

allSettled 最終一定是調用 then 方法:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('1111')
  }, 500)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('2222')
  }, 200)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('333')
  }, 700)
})

Promise.allSettled([p1, p2, p3, '---']).then(([res1, res2, res3, res4]) => {
  console.log(res1, res2, res3, res4)
})

最終打印出來的會是一個數組,返回各自的結果:

;[
  { status: 'fulfilled', value: '1111' },
  { status: 'fulfilled', value: '2222' },
  { status: 'rejected', reason: '333' },
  { status: 'fulfilled', value: '---' }
]

Promise.race

只要有一個 Promise 返回結果,就結束:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('1111')
  }, 500)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('2222')
  }, 200)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('333')
  }, 700)
})

Promise.race([p1, p2, p3]).then((res) => {
  // 2222
  console.log(res)
})

Promise.any

ES12 中新增的方法,只要有一個 Promise 返回的結果是成功的,就調用 then 方法,如果所有 Promise 都失敗,則調用 catch 方法,從 callback 中參數 errors 屬性能獲取 reject 返回值的陣列集合:

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('1111')
  }, 500)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('2222')
  }, 200)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('333')
  }, 700)
})

Promise.race([p1, p2, p3])
  .then((res) => {
    // 1111
    console.log(res)
  })
  .catch((err) => {
    // err: [AggregateError: All promises were rejected]
    console.log(err.errors)
  })

Promise 如何實現

promis A+ 是一個開放的規範,為如何設計一個開源的 Promise 制定一個共同的標準,會參考這些規範實現一個簡易的 Promise。

先創造一個 MyPromise 類:

class MyPromise {
  constructor(executor) {
    const resolve = () => {}

    const reject = () => {}

    executor(resolve, reject)
  }
}

定義 Promise 中不同階段的狀態:

const PROMISE_STATUS_PENDING = 'pedding'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    this.stauts = PROMISE_STATUS_PENDING

    const resolve = () => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        this.stauts = PROMISE_STATUS_FULFILLED
        console.log('resolve 被調用')
      }
    }

    const reject = () => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        this.stauts = PROMISE_STATUS_REJECTED
        console.log('reject 被調用')
      }
    }

    executor(resolve, reject)
  }
}

添加 then 方法:

const PROMISE_STATUS_PENDING = 'pedding'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    this.stauts = PROMISE_STATUS_PENDING
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFns = []
    this.onRejectedFns = []

    const resolve = (value) => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        // 使用微任務進行延遲以添加 then 的回調函數
        queueMicrotask(() => {
          if (this.stauts !== PROMISE_STATUS_PENDING) return
          this.stauts = PROMISE_STATUS_FULFILLED
          this.value = value
          this.onFulfilledFns.forEach((fn) => {
            fn(this.value)
          })
        })
      }
    }

    const reject = (reason) => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.stauts !== PROMISE_STATUS_PENDING) return
          this.stauts = PROMISE_STATUS_REJECTED
          this.reason = reason
          this.onRejectedFns.forEach((fn) => {
            fn(this.reason)
          })
        })
      }
    }

    // executor(resolve, reject)
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      // 如果在 then 調用的時候狀態已經確定了
      if (this.stauts === PROMISE_STATUS_FULFILLED && onFulfilled) {
        try {
          const value = onFulfilled(this.value)
          resolve(value)
        } catch (error) {
          reject(error)
        }
      }
      if (this.stauts === PROMISE_STATUS_REJECTED && onRejected) {
        try {
          const reason = onRejected(this.reason)
          resolve(reason)
        } catch (error) {
          reject(error)
        }
      }

      // 將成功和失敗的回調放到陣列中
      if (this.stauts === PROMISE_STATUS_PENDING) {
        this.onFulfilledFns.push(() => {
          try {
            const value = onFulfilled(this.value)
            resolve(value)
          } catch (error) {
            reject(error)
          }
        })
        this.onRejectedFns.push(() => {
          try {
            const reason = onRejected(this.reason)
            resolve(reason)
          } catch (error) {
            reject(error)
          }
        })
      }
    })
  }
}

加入 finally 和 catch API:

const PROMISE_STATUS_PENDING = 'pedding'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

function execFunctionWithCatchError(execFn, value, resolve, reject) {
  try {
    const result = execFn(value)
    resolve(result)
  } catch (error) {
    reject(error)
  }
}

class MyPromise {
  constructor(executor) {
    this.stauts = PROMISE_STATUS_PENDING
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFns = []
    this.onRejectedFns = []

    const resolve = (value) => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        // 使用微任務進行延遲以添加 then 的回調函數
        queueMicrotask(() => {
          if (this.stauts !== PROMISE_STATUS_PENDING) return
          this.stauts = PROMISE_STATUS_FULFILLED
          this.value = value
          this.onFulfilledFns.forEach((fn) => {
            fn(this.value)
          })
        })
      }
    }

    const reject = (reason) => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.stauts !== PROMISE_STATUS_PENDING) return
          this.stauts = PROMISE_STATUS_REJECTED
          this.reason = reason
          this.onRejectedFns.forEach((fn) => {
            fn(this.reason)
          })
        })
      }
    }

    // executor(resolve, reject)
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    // 如果沒有 onRejected 給予默認函數
    const defaultOnRejected = (err) => {
      throw err
    }
    onRejected = onRejected || defaultOnRejected

    const defaultOnFulfilled = (value) => value
    onFulfilled = onFulfilled || defaultOnFulfilled

    return new MyPromise((resolve, reject) => {
      // 如果在 then 調用的時候狀態已經確定了
      if (this.stauts === PROMISE_STATUS_FULFILLED && onFulfilled) {
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if (this.stauts === PROMISE_STATUS_REJECTED && onRejected) {
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }

      // 將成功和失敗的回調放到陣列中
      if (this.stauts === PROMISE_STATUS_PENDING) {
        if (onFulfilled)
          this.onFulfilledFns.push(() => {
            execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
          })
        if (onRejected)
          this.onRejectedFns.push(() => {
            execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
          })
      }
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  finally(onFinally) {
    this.then(
      () => {
        onFinally()
      },
      () => {
        onFinally()
      }
    )
  }
}

添加靜態方法:

const PROMISE_STATUS_PENDING = 'pedding'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

function execFunctionWithCatchError(execFn, value, resolve, reject) {
  try {
    const result = execFn(value)
    resolve(result)
  } catch (error) {
    reject(error)
  }
}

class MyPromise {
  constructor(executor) {
    this.stauts = PROMISE_STATUS_PENDING
    this.value = undefined
    this.reason = undefined
    this.onFulfilledFns = []
    this.onRejectedFns = []

    const resolve = (value) => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        // 使用微任務進行延遲以添加 then 的回調函數
        queueMicrotask(() => {
          if (this.stauts !== PROMISE_STATUS_PENDING) return
          this.stauts = PROMISE_STATUS_FULFILLED
          this.value = value
          this.onFulfilledFns.forEach((fn) => {
            fn(this.value)
          })
        })
      }
    }

    const reject = (reason) => {
      if (this.stauts === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.stauts !== PROMISE_STATUS_PENDING) return
          this.stauts = PROMISE_STATUS_REJECTED
          this.reason = reason
          this.onRejectedFns.forEach((fn) => {
            fn(this.reason)
          })
        })
      }
    }

    // executor(resolve, reject)
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    // 如果沒有 onRejected 給予默認函數
    const defaultOnRejected = (err) => {
      throw err
    }
    onRejected = onRejected || defaultOnRejected

    const defaultOnFulfilled = (value) => value
    onFulfilled = onFulfilled || defaultOnFulfilled

    return new MyPromise((resolve, reject) => {
      // 如果在 then 調用的時候狀態已經確定了
      if (this.stauts === PROMISE_STATUS_FULFILLED && onFulfilled) {
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if (this.stauts === PROMISE_STATUS_REJECTED && onRejected) {
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }

      // 將成功和失敗的回調放到陣列中
      if (this.stauts === PROMISE_STATUS_PENDING) {
        if (onFulfilled)
          this.onFulfilledFns.push(() => {
            execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
          })
        if (onRejected)
          this.onRejectedFns.push(() => {
            execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
          })
      }
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  finally(onFinally) {
    this.then(
      () => {
        onFinally()
      },
      () => {
        onFinally()
      }
    )
  }

  static resolve(value) {
    return new MyPromise((resolve) => resolve(value))
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
  }

  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const values = []
      promises.forEach((promise) => {
        promise.then(
          (res) => {
            values.push(res)
            if (values.length === promises.length) {
              resolve(values)
            }
          },
          (err) => {
            reject(err)
          }
        )
      })
    })
  }

  static allSettiled(promises) {
    return new MyPromise((resolve, reject) => {
      const results = []
      promises.forEach((promise) => {
        promise.then(
          (res) => {
            results.push({ status: PROMISE_STATUS_FULFILLED, value: res })
            if (results.length === promises.length) {
              resolve(results)
            }
          },
          (err) => {
            results.push({ status: PROMISE_STATUS_REJECTED, reason: err })
            if (results.length === promises.length) {
              resolve(results)
            }
          }
        )
      })
    })
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach((promise) => {
        promise.then(resolve, reject)
      })
    })
  }

  static any(promises) {
    // resolve 必須等到有一個成功的結果
    // reject 所有都失敗
    const reasons = []
    return new MyPromise((resolve, reject) => {
      promises.forEach((promise) => {
        promise.then(resolve, (err) => {
          reasons.push(err)
          if (reasons.length === promises.length) {
            reject(new AggregateError(reasons))
          }
        })
      })
    })
  }
}
Edit this page
Last Updated:
Contributors: louis, louis61619
Prev
Proxy 和 Reflect
Next
Iterator