前言

最开始的时候,没有分析需求,直接开始写。
写到一半发现没法符合异步需要。才发现还需要观察者模式帮忙。
这次从头开始。先分析需求。

一、需求明细

Promise.prototype上的属性

image.png

基本点
  • Promise是个构造器,接受一个函数作为参数
  • 有三种状态:pending/rejected/fulfilled
  • Prototype的几种基本方法:then(fulfilled执行)/catch(rejected执行)/finally(非pending执行)
  • 链式调用:需要返回 this
  • 需要观察者模式——以应对异步的情况
  • 一些方法的细节:就近原则,有 rejectedFn 则不走 catch,无则走 catch

    完善点
  • Promise还有以下方法:all、allSettled、race、any

二、代码

错误代码:没有考虑异步的情况。。。

第一个纯同步函数,执行成功。 第二个异步函数,没有执行成功。(原因是立即执行,这个时候 status => pending)

  1. const PENDING = 'pending'
  2. const FULFILLED = 'fulfilled'
  3. const REJECTED = 'rejected'
  4. function Promise(fn) {
  5. this.status = PENDING
  6. this.result = ''
  7. this.reason = ''
  8. this.resolve = (value) => {
  9. if (this.status === PENDING) {
  10. this.status = FULFILLED
  11. this.result = value
  12. }
  13. }
  14. this.reject = (value) => {
  15. if (this.status === PENDING) {
  16. this.status = REJECTED
  17. this.reason = value
  18. }
  19. }
  20. fn(this.resolve, this.reject)
  21. }
  22. Promise.prototype.then = function(resolveFunc, rejectFunc) {
  23. if (typeof resolveFunc === 'function' && this.status === FULFILLED) {
  24. resolveFunc(this.result)
  25. } else if (typeof rejectFunc === 'function' && this.status === REJECTED) {
  26. rejectFunc(this.reason)
  27. }
  28. return this
  29. }
  30. Promise.prototype.finally = function(finaFunc) {
  31. if (typeof finaFunc === 'function' && this.status !== PENDING) {
  32. finaFunc.call(this.result || this.reason)
  33. }
  34. }
  35. let p = new Promise(resolve => {
  36. resolve(true)
  37. }).then(res => {
  38. console.log('无异步', res)
  39. }).finally(res => {
  40. console.log('finally', res)
  41. })
  42. let pAsync = new Promise(resolve => {
  43. setTimeout(() => {
  44. console.log('god')
  45. resolve(true)
  46. }, 2000)
  47. }).then(res => {
  48. console.log('异步', res)
  49. }).finally(res => {
  50. console.log('finally:', res)
  51. })

结果

  • 同步函数正常执行
  • 异步函数,then、finally都没有执行

image.png

考虑异步的情况(利用观察者模式,在pending的时候订阅fulfilled-status、rejected-status)

错误代码:使用一个对象来存储“订阅者”的函数,多个 then 方法的情况会有问题

  1. const PENDING = 'pending'
  2. const FULFILLED = 'fulfilled'
  3. const REJECTED = 'rejected'
  4. function Promise(fn) {
  5. this.status = PENDING
  6. this.result = ''
  7. this.reason = ''
  8. this.resolveObj = null
  9. this.rejectedObj = null
  10. this.resolve = (value) => {
  11. if (this.status === PENDING) {
  12. this.status = FULFILLED
  13. this.result = value
  14. this.resolveObj && this.resolveObj(this.result)
  15. }
  16. }
  17. this.reject = (value) => {
  18. if (this.status === PENDING) {
  19. this.status = REJECTED
  20. this.reason = value
  21. this.rejectedObj && this.rejectedObj(this.reason)
  22. }
  23. }
  24. fn(this.resolve, this.reject)
  25. }
  26. Promise.prototype.then = function(resolveFunc, rejectFunc) {
  27. if (this.status === PENDING) {
  28. this.resolveObj = resolveFunc
  29. this.rejectedObj = rejectFunc
  30. } else if (typeof resolveFunc === 'function' && this.status === FULFILLED) {
  31. resolveFunc(this.result)
  32. return this
  33. } else if (typeof rejectFunc === 'function' && this.status === REJECTED) {
  34. rejectFunc(this.reason)
  35. return this
  36. }
  37. }
  38. let pDefer = new Promise(resolve => {
  39. setTimeout(() => {
  40. resolve('pDefer result')
  41. }, 1000)
  42. })
  43. pDefer.then(res => {
  44. console.log('第一个', res)
  45. })
  46. pDefer.then(res => {
  47. console.log('第二个', res)
  48. })

image.png
正确的结果应该是:
image.png

正确代码:使用数组来存放“订阅者”函数们

  1. const PENDING = 'pending'
  2. const FULFILLED = 'fulfilled'
  3. const REJECTED = 'rejected'
  4. function Promise(fn) {
  5. this.status = PENDING
  6. this.result = ''
  7. this.reason = ''
  8. this.resolveFnArr = []
  9. this.rejectFnArr = []
  10. this.resolve = (value) => {
  11. if (this.status === PENDING) {
  12. this.status = FULFILLED
  13. this.result = value
  14. this.resolveFnArr.forEach(fn => fn(this.result))
  15. }
  16. }
  17. this.reject = (value) => {
  18. if (this.status === PENDING) {
  19. this.status = REJECTED
  20. this.reason = value
  21. this.rejectFnArr.forEach(fn => fn(this.reason))
  22. }
  23. }
  24. try {
  25. fn(this.resolve, this.reject)
  26. } catch (error) {
  27. this.reject(error)
  28. }
  29. }
  30. Promise.prototype.then = function(resolveFunc, rejectFunc) {
  31. if (this.status === PENDING) {
  32. typeof resolveFunc === 'function' && this.resolveFnArr.push(resolveFunc)
  33. typeof rejectFunc === 'function' && this.rejectFnArr.push(rejectFunc)
  34. } else if (typeof resolveFunc === 'function' && this.status === FULFILLED) {
  35. resolveFunc(this.result)
  36. return this
  37. } else if (typeof rejectFunc === 'function' && this.status === REJECTED) {
  38. rejectFunc(this.reason)
  39. return
  40. }
  41. }
  42. Promise.prototype.finally = function(finaFunc) {
  43. if (typeof finaFunc === 'function' && this.status !== PENDING) {
  44. finaFunc.call(this.result || this.reason)
  45. }
  46. }
  47. let pDefer = new Promise(resolve => {
  48. setTimeout(() => {
  49. resolve('pDefer result')
  50. }, 1000)
  51. })
  52. pDefer.then(res => {
  53. console.log('第一个', res)
  54. })
  55. pDefer.then(res => {
  56. console.log('第二个', res)
  57. })

image.png
符合需要

至此,一个非链式的 Promise 完成了,链式的 Promise 比较复杂。。待掌握