Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

分步骤实现 A+ 规范的 Promise #55

Open
lovelmh13 opened this issue Jun 27, 2021 · 0 comments
Open

分步骤实现 A+ 规范的 Promise #55

lovelmh13 opened this issue Jun 27, 2021 · 0 comments

Comments

@lovelmh13
Copy link
Owner

lovelmh13 commented Jun 27, 2021

1. 初级版

function MyPromise(fn) {
  this.status = 'pending'
  this.val = ''
  this.error = ''
  this.fulfilledList = []
  this.rejectedList = []
  const resolve = (params) => {
    // 注意 this ,如果不暂存 this,就要用箭头函数
    if (this.status === 'pending') {
      this.status = 'fulfilled'
      this.val = params
    }
  }

  const reject = (error) => {
    if (this.status === 'pending') {
      this.status = 'rejected'
      this.error = error
    }
  }

  fn(resolve, reject)
}

MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
  if (this.status === 'fulfilled') {
    fulfilledFn(this.val)
  }
  if (this.status === 'rejected') {
    rejectedFn(this.error)
  }
}

new MyPromise((resolve, reject) => {
  resolve(1)
}).then(
  (data) => {
    console.log(data)
  },
  (err) => {
    console.log(err)
  }
)

2. 支持异步版

function MyPromise(fn) {
  this.status = 'pending'
  this.val = ''
  this.error = ''
  this.fulfilledList = []
  this.rejectedList = []
  const resolve = (params) => {
    // 注意 this ,如果不暂存 this,就要用箭头函数
    if (this.status === 'pending') {
      this.status = 'fulfilled'
      this.val = params
      this.fulfilledList.forEach((fn) => {
        fn(this.val)
      })
    }
  }

  const reject = (error) => {
    if (this.status === 'pending') {
      this.status = 'rejected'
      this.error = error
      this.rejectedList.forEach((fn) => {
        fn(this.error)
      })
    }
  }

  fn(resolve, reject)
}

MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
  if (this.status === 'fulfilled') {
    fulfilledFn(this.val)
  }
  if (this.status === 'rejected') {
    rejectedFn(this.error)
  }
  if (this.status === 'pending') {
    this.fulfilledList.push(fulfilledFn)
    this.rejectedList.push(rejectedFn)
  }
}

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  }, 1000)
}).then(
  (data) => {
    console.log(data)
  },
  (err) => {
    console.log(err)
  }
)

3. 链式调用版

不支持 then 里再返回 Promise 的 版本

    function MyPromise(fn) {
        this.status = 'pending'
        this.val = ''
        this.error = ''
        this.fulfilledList = []
        this.rejectedList = []
        const resolve = (params) => {
          // 注意 this ,如果不暂存 this,就要用箭头函数
          if (this.status === 'pending') {
            this.status = 'fulfilled'
            this.val = params
            this.fulfilledList.forEach((fn) => {
              fn(this.val)
            })
          }
        }

        const reject = (error) => {
          if (this.status === 'pending') {
            this.status = 'rejected'
            this.error = error
            this.rejectedList.forEach((fn) => {
              fn(this.error)
            })
          }
        }

        fn(resolve, reject)
      }

      MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
        return new MyPromise((resolve, reject) => {
          // 成功的回调
          const fulfilledCb = () => {
            queueMicrotask(() => {
              // 别忘了 then 是微任务, queueMicrotask 在 window 对象上才有
              try {
                const res = fulfilledFn(this.val)
                resolve(res)
              } catch (err) {
                reject(err)
              }
            })
          }
          // 错误的回调
          const rejectedCb = () => {
            queueMicrotask(() => {
              try {
                const res = rejectedFn(this.error) // 注意这里,虽然是 rejected 状态,但是回调返回的值,如果不是错误,依然会走 then 的 MyPromise 的
                resolve(res)
              } catch (error) {
                reject(error)
              }
            })
          }

          if (this.status === 'fulfilled') {
            fulfilledCb()
          }
          if (this.status === 'rejected') {
            rejectedCb()
          }
          if (this.status === 'pending') {
            this.fulfilledList.push(fulfilledCb)
            this.rejectedList.push(rejectedCb)
          }
        })
      }

      new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve(1)
        }, 1000)
      })
        .then(
          (data) => {
            console.log(data)
            return '第一个完成'
          },
          (err) => {
            console.log(err)
          }
        )
        .then((data) => {
          console.log(data)
        })

      console.log('同步函数')

支持 then 里返回 Promise 的版本

function MyPromise(fn) {
  this.status = 'pending'
  this.val = ''
  this.error = ''
  this.fulfilledList = []
  this.rejectedList = []
  const resolve = (value) => {
    if (value instanceof MyPromise) {
      return value.then(resolve, reject) // 细节,适用于 then 里又 return Promise.resolve() 的情况
    }
    // 注意 this ,如果不暂存 this,就要用箭头函数
    if (this.status === 'pending') {
      this.status = 'fulfilled'
      this.val = value
      this.fulfilledList.forEach((fn) => {
        fn(this.val)
      })
    }
  }
  const reject = (error) => {
    if (this.status === 'pending') {
      this.status = 'rejected'
      this.error = error
      this.rejectedList.forEach((fn) => {
        fn(this.error)
      })
    }
  }
  fn(resolve, reject)
}
MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
  const thenPromise = new MyPromise((resolve, reject) => {
    // 成功的回调
    const fulfilledCb = () => {
      queueMicrotask(() => {
        // 别忘了 then 是微任务, queueMicrotask 在 window 对象上才有
        try {
          const res = fulfilledFn(this.val)
          resolvePromise(thenPromise, res, resolve, reject)
        } catch (err) {
          reject(err)
        }
      })
    }
    // 错误的回调
    const rejectedCb = () => {
      queueMicrotask(() => {
        try {
          const res = rejectedFn(this.error) // 注意这里,虽然是 rejected 状态,但是回调返回的值,如果不是错误,依然会走 then 的 MyPromise 的
          resolvePromise(thenPromise, res, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
    }
    if (this.status === 'fulfilled') {
      fulfilledCb()
    }
    if (this.status === 'rejected') {
      rejectedCb()
    }
    if (this.status === 'pending') {
      this.fulfilledList.push(fulfilledCb)
      this.rejectedList.push(rejectedCb)
    }
  })
  return thenPromise
}
// 处理当 then 里又 return Promise 的情况
const resolvePromise = (thenPromise, result, resolve, reject) => {
  if (thenPromise === result) {
    return reject(new TypeError('error due to circular reference')) // 死循环
  }
  let called // 防止多次调用(这个变量其实不知道是为什么需要的)
  if (
    result !== null &&
    (typeof result === 'object' || typeof result === 'function')
  ) {
    try {
      const thenable = result.then // 鸭子辨型,如果有 then 属性,就会被认为是 Promise
      if (typeof thenable === 'function') {
        thenable.call(
          result,
          (data) => {
            if (called) {
              return
            }
            called = true
            resolvePromise(thenPromise, data, resolve, reject) // 返回的可能是一个 Promise 实例或者是一个普通值,所以就递归
          },
          (error) => {
            if (called) {
              return
            }
            called = true
            reject(error)
          }
        )
      } else {
        resolve(result)
      }
    } catch (error) {
      if (called) {
        return
      }
      called = true // 防止调用失败后又调用成功
      reject(error)
    }
  } else {
    // 普通的值, 或者没有
    resolve(result)
  }
}
// -------------------- test --------------------
// -------------------- 死循环的例子 --------------------
// const promise = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('lucas')
//   }, 2000)
// })
// promise
//   .then(
//     (onfulfilled = (data) => {
//       console.log(data)
//       return onfulfilled(data)
//     })
//   )
//   .then((data) => {
//     console.log(data)
//   })
// -------------------- 需要递归的例子 --------------------
const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('lucas')
  }, 1000)
})
promise
  .then((data) => {
    console.log(data)
    return new MyPromise((resolve, reject) => {
      setTimeout(() => {
        resolve(`${data} next then`)
      }, 2000)
    }).then((data) => {
      return new MyPromise((resolve, reject) => {
        setTimeout(() => {
          resolve(`${data} next then`)
        }, 2000)
      })
    })
  })
  .then((data) => {
    console.log(data)
  })

4. 支持 Promise 穿透的版本

什么是 Promise穿透:

// -------------------- 穿透的例子 --------------------
const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('lucas')
  }, 2000)
})
promise.then(null).then((data) => {
  console.log(data)
})
/**
 * 这段代码将会在 2 秒后输出:lucas。这就是 Promise 穿透现象:
 * 给 .then() 函数传递非函数值作为其参数时,实际上会被解析成 .then(null),这时候的表现应该是:
 * 上一个 promise 对象的结果进行“穿透”,
 * 如果在后面链式调用仍存在第二个 .then() 函数时,将会获取被穿透下来的结果。
 **/
function MyPromise(fn) {
  this.status = 'pending'
  this.val = ''
  this.error = ''
  this.fulfilledList = []
  this.rejectedList = []
  const resolve = (value) => {
    if (value instanceof MyPromise) {
      return value.then(resolve, reject) // 细节,适用于 then 里又 return Promise.resolve() 的情况
    }
    // 注意 this ,如果不暂存 this,就要用箭头函数
    if (this.status === 'pending') {
      this.status = 'fulfilled'
      this.val = value
      this.fulfilledList.forEach((fn) => {
        fn(this.val)
      })
    }
  }
  const reject = (error) => {
    if (this.status === 'pending') {
      this.status = 'rejected'
      this.error = error
      this.rejectedList.forEach((fn) => {
        fn(this.error)
      })
    }
  }
  fn(resolve, reject)
}
MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
  // 解决 fulfilledFn, rejectedFn 没有传值的问题(Promise 穿透),给一个默认值,就可以继续往下执行了
  fulfilledFn =
    typeof fulfilledFn === 'function' ? fulfilledFn : (data) => data
  rejectedFn =
    typeof rejectedFn === 'function'
      ? rejectedFn
      : (error) => {
          throw error
        }
  const thenPromise = new MyPromise((resolve, reject) => {
    // 成功的回调
    const fulfilledCb = () => {
      queueMicrotask(() => {
        // 别忘了 then 是微任务, queueMicrotask 在 window 对象上才有
        try {
          const res = fulfilledFn(this.val)
          resolvePromise(thenPromise, res, resolve, reject)
        } catch (err) {
          reject(err)
        }
      })
    }
    // 错误的回调
    const rejectedCb = () => {
      queueMicrotask(() => {
        try {
          const res = rejectedFn(this.error) // 注意这里,虽然是 rejected 状态,但是回调返回的值,如
          resolvePromise(thenPromise, res, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
    }
    if (this.status === 'fulfilled') {
      fulfilledCb()
    }
    if (this.status === 'rejected') {
      rejectedCb()
    }
    if (this.status === 'pending') {
      this.fulfilledList.push(fulfilledCb)
      this.rejectedList.push(rejectedCb)
    }
  })
  return thenPromise
}
// 处理当 then 里又 return Promise 的情况
const resolvePromise = (thenPromise, result, resolve, reject) => {
  if (thenPromise === result) {
    return reject(new TypeError('error due to circular reference')) // 死循环
  }
  let called // 防止多次调用(这个变量其实不知道是为什么需要的)
  if (
    result !== null &&
    (typeof result === 'object' || typeof result === 'function')
  ) {
    try {
      const thenable = result.then // 鸭子辨型,如果有 then 属性,就会被认为是 Promise
      if (typeof thenable === 'function') {
        thenable.call(
          result,
          (data) => {
            if (called) {
              return
            }
            called = true
            resolvePromise(thenPromise, data, resolve, reject) // 返回的可能是一个 Promise 实例或者
          },
          (error) => {
            if (called) {
              return
            }
            called = true
            reject(error)
          }
        )
      } else {
        resolve(result)
      }
    } catch (error) {
      if (called) {
        return
      }
      called = true // 防止调用失败后又调用成功
      reject(error)
    }
  } else {
    // 普通的值, 或者没有
    resolve(result)
  }
}

用 promises-aplus-tests 测试 A+

注意,如果用这个测试,需要把代码里的 queueMicrotask 改成 setTimeout,因为 queueMicrotask 是在 window 上才有的

先全部安装 promises-aplus-tests

然后在 MyPromise 的文件后面加上:

MyPromise.defer = MyPromise.deferred = function () {
  let dfd = {}
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
module.exports = MyPromise

最后在命令行执行 promises-aplus-tests ./A+test.js

5. Promise 的各种 API 实现

// -------------------- catch  finally 都是在原型上的方法,都是返回一个 then --------------------
MyPromise.prototype.catch = function (cb) {
  return this.then(null, cb)
}
MyPromise.prototype.finally = function (cb) {
  return this.then(
    (value) => {
      return MyPromise.resolve(cb()).then(() => value)
    },
    (error) => {
      return MyPromise.resolve(cb()).then(() => {
        throw error
      })
    }
  )
}
// -------------------- resolve reject all any race 都是在实例上的方法,都是返回一个 MyPromise 实例 --------------------
MyPromise.resolve = function (val) {
  return new MyPromise((resolve) => {
    resolve(val)
  })
}
MyPromise.reject = function (err) {
  return new MyPromise((resolve, reject) => {
    reject(err)
  })
}
// ------------------- all any race 都是在实例上的方法,都是返回一个 MyPromise 实例,且都在返回的实例执行循环,使用MyPromise.resolve().then() 返回结果 --------------------
MyPromise.all = function (iterable) {
  if (!iterable[Symbol.iterator]) {
    return new TypeError(`${iterable} is not iterable`)
  }
  const arr = Array.from(iterable)
  const res = []
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < arr.length; i++) {
      // 保证顺序
      const curr = arr[i]
      MyPromise.resolve(curr).then(
        (val) => {
          res[i] = val
          if (i === arr.length - 1) {
            resolve(res)
          }
        },
        (error) => {
          reject(error)
        }
      )
    }
  })
}
MyPromise.any = function (iterable) {
  if (!iterable[Symbol.iterator]) {
    return new TypeError(`${iterable} is not iterable`)
  }
  const arr = Array.from(iterable)
  const res = []
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < arr.length; i++) {
      const curr = arr[i]
      MyPromise.resolve(curr).then(
        (val) => {
          resolve(val)
        },
        (error) => {
          res[i] = error
          if (i === arr.length - 1) {
            reject(res)
          }
        }
      )
    }
  })
}
// 返回第一个已经结束的状态
MyPromise.race = function (iterable) {
  if (!iterable[Symbol.iterator]) {
    return new TypeError(`${iterable} is not iterable`)
  }
  const arr = Array.from(iterable)
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < arr.length; i++) {
      const curr = arr[i]
      MyPromise.resolve(curr).then(resolve, reject)
    }
  })
}
// -------------------- test --------------------
// -------------------- all --------------------
// const p1 = new MyPromise((resolve, reject) => {
//   resolve(1)
// })
// const p2 = new MyPromise((resolve, reject) => {
//   resolve(2)
// })
// const p3 = '123'
// const p4 = MyPromise.resolve(4)
// MyPromise.all([p1, p2, p3, p4]).then(
//   (data) => {
//     console.log(data)
//   },
//   (error) => {
//     console.log(error)
//   }
// )
// -------------------- any --------------------
// const pErr = new MyPromise((resolve, reject) => {
//   reject('总是失败')
// })
// const pSlow = new MyPromise((resolve, reject) => {
//   setTimeout(resolve, 500, '最终完成')
// })
// const pFast = new MyPromise((resolve, reject) => {
//   setTimeout(resolve, 100, '很快完成')
// })
// MyPromise.any([pErr, pSlow, pFast]).then((value) => {
//   console.log(value)
// })
// 期望输出: "很快完成"
// const pErr = new MyPromise((resolve, reject) => {
//   reject('总是失败')
// })
// MyPromise.any([pErr]).then(
//   (val) => {
//     console.log('val: ', val)
//   },
//   (err) => {
//     console.log(err)
//   }
// )
// -------------------- race --------------------
var p1 = new MyPromise(function (resolve, reject) {
  setTimeout(resolve, 500, 'one')
})
var p2 = new MyPromise(function (resolve, reject) {
  setTimeout(reject, 100, 'two')
})
MyPromise.race([p1, p2]).then(
  function (value) {
    console.log('value: ', value) // "two"
  },
  function (err) {
    console.log('err: ', err)
  }
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant