Skip to content
On this page

还不知道Promise是啥?

还不知道Promise是啥?

参考文章

还不知道Promise是啥?

可能是目前最易理解的手写promise

手写一个Promise/A+,完美通过官方872个测试用例

js
function Promise(fn) {
  this.state = 'pending' // 状态
  this.res = undefined   // 成功的返回值
  this.err = undefined   // 失败的返回值
  this.fn1Callbacks = []
  this.fn2Callbacks = []

  const resolve = value => {
    setTimeout(() => {
      if (this.state === 'pending') {
        this.state = 'resolved'
        this.value = value
        for (let i = 0; i < this.fn1Callbacks.length; i++) {
          this.fn1Callbacks[i](value);
        }
      }
    })
  }
  const reject = error => {
    setTimeout(() => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.err = error
        for (let i = 0; i < this.fn2Callbacks.length; i++) {
          this.fn2Callbacks[i](error);
        }
      }
    })
  }

  try {
    fn(resolve, reject)
  } catch (error) {
    reject(error)
  }

}

function resolvePromise(otherPromise, x, resolve, reject) {
  if (otherPromise === x) {
    return reject(new Error('promise loop'))
  }
  if (x instanceof Promise) {
    x.then(value => {
      resolve(value)
    }, error => {
      reject(error)
    })
    return
  }
  if (x instanceof Object || typeof x === 'function') {
    try {
      const then = x.then
      let called

      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) {
            return
          }
          called = true
          return resolvePromise(otherPromise, y, resolve, reject)
        }, r => {
          if (called) {
            return
          }
          called = true
          return reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (error) {
      if (called) {
        return
      }
      reject(error)
    }
  } else {
    resolve(x)
  }
}

Promise.prototype.then = function (fn1, fn2) {
  const _this = this
  let otherpro
  fn1 = typeof fn1 === 'function' ? fn1 : function (value) { }
  fn2 = typeof fn2 === 'function' ? fn2 : function (error) { }
  if (this.state === 'resolved') {
    return otherpro = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          const x = fn1(_this.value)
          resolvePromise(otherpro, x, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
    })
  } else if (this.state === 'rejected') {
    return otherpro = new Promise(function (resolve, reject) {
      setTimeout(() => {
        try {
          const x = fn2(_this.err)
          // reject(x)
          resolvePromise(otherpro, x, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
    })
  } else {
    return otherpro = new Promise(function (resolve, reject) {
      _this.fn1Callbacks.push(function () {
        try {
          const x = fn1(_this.value)
          // resolve(x)
          resolvePromise(otherpro, x, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
      _this.fn2Callbacks.push(function () {
        try {
          const x = fn2(_this.err)
          // reject(x)
          resolvePromise(otherpro, x, resolve, reject)
        } catch (error) {
          reject(error)
        }
      })
    })
  }

}

Promise.prototype.catch = function (fn) {
  this.then(null, fn)
}

Promise.prototype.finally = function (fn) {
  return this.then(res => {
    return new Promise(resolve => {
      resolve(fn())
    }).then(() => {
      return res
    })
  }).catch(err => {
    return new Promise(resolve => {
      resolve(fn())
    }).then(() => {
      throw err
    })
  })
}

Promise.resolve = function (params) {
  if (params instanceof Promise) {
    return params
  }
  return new Promise(function (resolve) {
    resolve(params)
  })
}

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

Promise.all = function (promises) {
  return new Promise(function (resolve, reject) {
    if (!promises || !Array.isArray(promises)) {
      reject('promises must be a array')
    }
    if (promises.length === 0) {
      return resolve([])
    }
    const data = []
    let num = 0
    promises.forEach(item => {
      item.then(res => {
        data.push(res)
        num++
        if (num === promises.length) {
          resolve(data)
        }
      }).catch(err => {
        reject(err)
      })
    })
  })
}

Promise.race = function (promises) {
  return new Promise(function (resolve, reject) {
    if (!promises || !Array.isArray(promises)) {
      reject('promises must be a array')
    }
    if (promises.length === 0) {
      return resolve()
    }
    promises.forEach(item => {
      item.then(res => {
        data.push(res)
        resolve(res)
      }).catch(err => {
        reject(err)
      })
    })
  })
}




new Promise((resolve, reject) => {
  resolve('aaa')
}).then(val => {
  console.log(val)
}).finally(val => {
  console.log(val)
  setTimeout(() => {
    console.log(val)
  })
})