qiuhongbingo / blog

Writing something in Issues.
https://github.com/qiuhongbingo/blog/issues
3 stars 0 forks source link

Promise 手写实现??? #13

Open qiuhongbingo opened 4 years ago

qiuhongbingo commented 4 years ago
function Promise(executor) {
  this.status = 'pending' // pending -> fulfilled 或者 pending -> rejected 一次性变化,不可逆转

  this.value = null // resolve 的值
  this.reason = null // reject 的值

  // 针对 call 4,需要保存 then/catch 的方法数组,兼容多个 then/catch
  this.onFulfilledArray = []
  this.onRejectedArray = []

  const resolve = value => {
    // 兼容 then 的链式调用
    if (value instanceof Promise) return value.then(resolve, reject)
    // 针对 call 2,利用 setTimeout 推入队列(宏任务),不阻塞主线程执行
    setTimeout(() => {
      if (this.status === 'pending') {
        this.value = value
        this.status = 'fulfilled'
        this.onFulfilledArray.forEach(func => func(value))
      }
    })
  }

  const reject = reason => {
    setTimeout(() => {
      if (this.status === 'pending') {
        this.reason = reason
        this.status = 'rejected'
        this.onRejectedArray.forEach(func => func(reason))
      }
    })
  }

  // 如果出错,将会自动触发 rejected
  try {
    executor(resolve, reject)
  } catch (e) {
    reject(e)
  }
}

const resolvePromise = (promise2, result, resolve, reject) => {
  // 当 result 和 promise2 相等时,也就是说 onfulfilled 返回 promise2 时,进行 reject
  // 针对 call 6 的死循环处理方案
  if (result === promise2) reject(new TypeError('error due to circular reference'))

  // 是否已经执行过 onfulfilled 或者 onrejected
  let consumed = false
  let thenable

  if (result instanceof Promise) {
    if (result.status === 'pending') {
      result.then(data => resolvePromise(promise2, data, resolve, reject), reject)
    } else {
      result.then(resolve, reject)
    }
    return
  }

  // 判断返回结果是否普通值
  let isComplexResult = target => (typeof target === 'function' || typeof target === 'object') && target !== null

  // 如果返回的是疑似 Promise 类型
  if (isComplexResult(result)) {
    try {
      thenable = result.then
      // 如果返回的是 Promise 类型,具有 then 方法
      if (typeof thenable === 'function') {
        thenable.call(
          result,
          data => {
            if (consumed) return
            consumed = true
            // 针对 call 7 的 Promise 多级链式调用,这里用上递归
            return resolvePromise(promise2, data, resolve, reject)
          },
          error => {
            if (consumed) return
            consumed = true
            return reject(error)
          }
        )
      } else {
        resolve(result)
      }
    } catch (e) {
      if (consumed) {
        return
      }
      consumed = true
      return reject(e)
    }
  } else {
    // 普通值直接返回
    resolve(result)
  }
}

// then 之所以挂载在原型上,是基于性能考虑,避免重复实例多次
Promise.prototype.then = function(onfulfilled, onrejected) {
  // 针对 call 8,兼容 .then(null) 也不影响链式调用,从而实现穿透
  onfulfilled =
    typeof onfulfilled === 'function'
      ? onfulfilled
      : data => {
          return data
        }
  onrejected =
    typeof onrejected === 'function'
      ? onrejected
      : error => {
          throw error
        }

  let promise2

  if (this.status === 'fulfilled') {
    return (promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          // 这个新的 promse2 resolved 的值为 onfulfilled 的执行结果
          let result = onfulfilled(this.value)
          resolvePromise(promise2, result, resolve, reject)
        } catch (e) {
          reject(e)
        }
      })
    }))
  }

  if (this.status === 'rejected') {
    return (promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          // 这个新的 promise2 reject 的值为 onrejected 的执行结果
          let result = onrejected(this.value)
          resolvePromise(promise2, result, resolve, reject)
        } catch (e) {
          reject(e)
        }
      })
    }))
  }

  if (this.status === 'pending') {
    return (promise2 = new Promise((resolve, reject) => {
      this.onFulfilledArray.push(() => {
        try {
          let result = onfulfilled(this.value)
          resolvePromise(promise2, result, resolve, reject)
        } catch (e) {
          reject(e)
        }
      })
      this.onRejectedArray.push(() => {
        try {
          let result = onrejected(this.reason)
          resolvePromise(promise2, result, resolve, reject)
        } catch (e) {
          reject(e)
        }
      })
    }))
  }
}

Promise.prototype.catch = function(catchFunc) {
  // 利用 then 的第二个参数进行异常捕获
  return this.then(null, catchFunc)
}

// 针对 call 10
Promise.resolve = function(value) {
  return new Promise((resolve, reject) => {
    resolve(value)
  })
}

Promise.reject = function(value) {
  return new Promise((resolve, reject) => {
    reject(value)
  })
}

// 针对 call 11
Promise.all = function(promiseArray) {
  if (!Array.isArray(promiseArray)) throw new TypeError('The arguments should be an array!')

  return new Promise((resolve, reject) => {
    try {
      let resultArray = []
      const length = promiseArray.length
      for (let i = 0; i < length; i++) {
        promiseArray[i].then(data => {
          resultArray.push(data)
          if (resultArray.length === length) resolve(resultArray)
        }, reject)
      }
    } catch (e) {
      reject(e)
    }
  })
}

// 针对 call 12
Promise.race = function(promiseArray) {
  if (!Array.isArray(promiseArray)) throw new TypeError('The arguments should be an array!')
  return new Promise((resolve, reject) => {
    try {
      const length = promiseArray.length
      for (let i = 0; i < length; i++) promiseArray[i].then(resolve, reject) // 谁快谁 resolve
    } catch (e) {
      reject(e)
    }
  })
}

// call 1
// const promise = new Promise((resolve, reject) => {
//   resolve('data')
//   reject('error')
// })

// promise.then(
//   data => {
//     console.log(data) // 会执行
//   },
//   error => {
//     console.log(error) // 不会执行
//   }
// )

// call 2
// const promise = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('data')
//   }, 2000)
// })

// promise.then(data => {
//   console.log(data) // 2s 后输出 data
// })

// call 3
// const promise = new Promise((resolve, reject) => {
//   resolve('data')
// })

// promise.then(data => {
//   console.log(data) // 后执行
// })

// console.log(1) // 先执行

// call 4
// const promise = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('data')
//   }, 2000)
// })

// promise.then(data => {
//   console.log(`1: ${data}`) // 2s 后输出 1: data
// })
// promise.then(data => {
//   console.log(`2: ${data}`) // 2s 后输出 2: data
// })

// call 5
// const promise = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('bingo')
//   }, 2000)
// })

// promise
//   .then(data => {
//     console.log(data) // 2s 后输出 bingo
//     return `${data} next then`
//   })
//   .then(data => {
//     console.log(data) // 紧接输出 bingo next then
//   })

// call 6
// const promise = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('bingo')
//   }, 2000)
// })

// promise
//   .then(
//     (onfulfilled = data => {
//       console.log(data)
//       return onfulfilled(data) // 模拟死循环
//     })
//   )
//   .then(data => {
//     console.log(data)
//   })

// call 7
// const promise = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('bingo')
//   }, 2000)
// })

// promise
//   .then(data => {
//     console.log(data)
//     return new Promise((resolve, reject) => {
//       setTimeout(() => {
//         resolve(`${data} next then`)
//       }, 4000)
//     }).then(data => {
//       return new Promise((resolve, reject) => {
//         setTimeout(() => {
//           resolve(`${data} next then`)
//         }, 4000)
//       })
//     })
//   })
//   .then(data => {
//     console.log(data)
//   })

// call 8
// const promise = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('bingo')
//   }, 2000)
// })

// promise.then(null).then(data => { // 穿透实现
//   console.log(data)
// })

// call 9
// const promise = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     reject('bingo error')
//   }, 2000)
// })

// promise
//   .then(data => {
//     console.log(data)
//   })
//   .catch(error => {
//     console.log(error) // 2s 后输出 bingo error
//   })

// call 10
// Promise.resolve('data').then(data => {
//   console.log(data) // 再输出 data
// })
// console.log(1) // 先输出 1

// call 11
// const promise1 = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('bingo 1')
//   }, 2000)
// })

// const promise2 = new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('bingo 2')
//   }, 2000)
// })

// Promise.all([promise1, promise2]).then(data => {
//   console.log(data) // 2s 后输出 ['bingo 1', 'bingo 2']
// })

// call 12
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('bingo 1')
  }, 2000)
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('bingo 2')
  }, 4000)
})

Promise.race([promise1, promise2]).then(data => {
  console.log(data) // 2s 后输出 bingo 1
})