Promise原理、方法及手写

Promise原理、方法及手写

ES6Promise对象:Promise 对象 - ECMAScript 6入门 (ruanyifeng.com)

Promise/A+链接:Promises/A+ (promisesaplus.com)

什么是Promise?

Promise是异步编程的一种解决方案,代表一个异步操作的最终完成或者失败。

Promise的三种状态

一个Promise必然会处于以下三种状态之一:

  • 未完成(pending):初始状态,即没有成功,也没有失败。
  • 成功(fufilled):表示异步操作成功。
  • 失败(rejected):表示异步操作失败。

Promise的状态由异步操作的结果来决定,当状态发生改变时,就无法再次改变。

Promise的状态只能从未完成(pending)转变成成功(fufilled)或者是从未完成(pending)转变成失败(rejected)

基本用法

Promise对象是一个构造函数,通过new Promise来创建实例对象

const promise = new Promise((resolve, reject) => {
  // some code 
  if(/*判断条件*/) {
    resolve(res) // res为成功的结果
  } else {
    reject(err) // err为失败的原因
  }
})

Promise构造函数接收一个回调函数作为参数,该函数的两个参数分别为resolve和reject。

resolve函数的作用是将Promise对象的状态从"未完成"变成"成功",在异步操作完成时进行调用,并将异步操作的结果,作为参数传递出去。

reject函数的作用是将Promise对象的状态从"未完成"变成"失败",在异步操作完成时进行调用,并将异步操作的结果,作为参数传递出去。

Promise.then()

Promise实例具有then方法,该方法的第一个参数是成功状态下的回调函数,第二个参数是失败状态下的回调函数,同时该方法返回的是一个新的Promise实例。

当Promise实例的状态为成功时,则不会调用then方法中失败的回调函数。

const promise = new Promise((resolve, reject) => {
  resolve(1)
}).then(
  (res) => { console.log(res) },
  (err) => { console.log(err) }
)
// 打印结果为1

非链式调用的then,可以对同一个Promise实例可以多次调用then方法。

const promise = new Promise((resolve, reject) => {
  resolve(1)
})
promise.then(
  (res) => { console.log(res) }
)
promise.then(
  (res) => { console.log(res) }
)
/*
	打印结果为
	1
	1
*/

链式调用的then,then方法默认返回的是Promise对象,所以在then方法后还可以继续使用then方法。

第一个then方法的回调函数执行完成后,会继续执行第二个then方法的回调函数。

const promise = new Promise((resolve, reject) => {
  resolve()
}).then(
  (res) => { console.log(1) }
).then(
  (res) => { console.log(2) }
)
/*
	打印结果为
	1
	2
*/

当第一个then方法不返回或者返回的是一个非thenable对象时,返回的值都只能在下一次then方法的成功回调中接收。

const promise = new Promise((resolve, reject) => {
  reject()
}).then(
  (res) => { },
  (err) => 1
).then(
  (res) => { console.log(res) }
)
// 打印结果为 1

thenable对象:

  • Promise
  • { then(){} }
  • function a(){} a.then = function(){}

具有then方法为thenable对象

当第一个then方法返回是一个thenable对象时,第二个then方法会等第一个then方法返回的thenable对象状态发生改变时,再去执行回调函数。如果状态为fufilled时,会调用第一个回调函数,如果状态为rejected时,会调用第二个回调函数。

const promise = new Promise((resolve, reject) => {
  resolve()
}).then(
  (res) => new Promise((resolve, reject) => {
    reject()
  }),
).then(
  (res) => { console.log(1) },
  (err) => { console.log(2) }
)
// 打印结果为 2

Promise.catch()

Promise实例具有catch方法,当实例失败时会调用该方法的回调函数,与then方法中的第二个回调方法效果一致。如果then方法指定的回调函数发生错误,也会被捕获。

const promise = new Promise((resolve, reject) => {
  reject()
}).then(
  (res) => { }
).catch(
  (err) => { console.log(1) }
)

const promise = new Promise((resolve, reject) => {
  resolve()
}).then(
  (res) => new Promise((resolve, reject) => {
    reject()
  })
).catch(
  (err) => { console.log(1) }
)
// 打印结果均为 1

Promise.finally()

finally方法用于不管Promise实例对象最后的状态如何,都会执行回调函数。

finally方法的回调函数不接收任何参数,所以无法从finally方法中判断promise实例的状态是fufilled还是rejected。

const promise = new Promise((resolve, reject) => {
  resolve()
}).then(
  (res) => { console.log(1) }
).finally(
  () => { console.log(2) }
)

const promise = new Promise((resolve, reject) => {
  reject()
}).then(
  (res) => { },
  (err) => { console.log(1) }
).finally(
  () => { console.log(2) }
)
/*
	打印结果均为
	1
	2
*/

Promise.all()

Promise.all()方法接收一个可迭代(数组或字符串)的Promise实例,其内部元素也可以是Promise实例,也可以不是。如果传入的Promise实例的状态都为fufilled,那么返回的是一个数组,数组中的元素分别为成功的结果。如果传入的Promise实例的状态有一个为rejected,那么返回的是第一个失败的结果。

成功的情况下,输入的数组元素与输出的数组元素一一对应,输入的数组的第一个元素的结果为输出的数组的第一个元素。如果数组中存在异步操作,会等待异步操作完成后在输出结果。

const promise1 = 1
const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve(2)
  }, 1000)
})
const promise3 = Promise.resolve(3)
const promise = [promise1, promise2, promise3]
Promise.all(promise).then(res => {
  console.log(res);
})
// 打印结果为 [1, 2, 3]
const promise1 = 1
const promise2 = new Promise((resolve, reject) => { throw new Error('报错了') })
const promise3 = Promise.resolve(3)
const promise = [promise1, promise2, promise3]
Promise.all(promise).then(res => {
  console.log(res);
})
// 打印结果为 Error: 报错了

Promise.race()

Promise.race()方法和Promise.all()方法一样接收的都是一个可迭代的实例,此方法并不会返回全部的结果,只会返回第一个成功或者失败的结果,其他效果和Promise.all()方法一样。

const promise1 = 1
const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve(2)
  }, 1000)
})
const promise = [promise1, promise2]
Promise.race(promise).then(res => {
  console.log(res);
})
// 打印结果为 1
const promise1 = Promise.reject(1)
const promise2 = new Promise((resolve) => {
  setTimeout(() => {
     resolve(2)
  }, 1000)
})
const promise = [promise1, promise2]
Promise.race(promise).then(res => {
  console.log(res);
})
// 打印结果为 Uncaught (in promise) 1

Promise.allSettled()

Promise.allSettled()方法传入的也是一个可以迭代的promise实例,此方法不论结果为fufilled和rejected都会把结果和状态返回。

const promise1 = 1
const promise2 = new Promise((resolve) => {
  setTimeout(() =1> {
    resolve(2)
  }, 1000)
})
const promise3 = Promise.reject(3)
const promise = [promise1, promise2, promise3]
Promise.allSettled(promise).then(res => {
  console.log(res);
})
/*
打印结果为
(3) [{…}, {…}, {…}]
	0: {status: 'fulfilled', value: 1}
	1: {status: 'fulfilled', value: 2}
	2: {status: 'rejected', reason: 3}
*/

Promise.any()

Promise.any()方法和Promise.race()方法很像,也是输出最快的结果,但是并不会因为某一个Promise实例的状态为rejected而结束,当所有的Promise实例的状态都为rejected时才会结束。

const promise1 = 1
const promise2 = new Promise(resolve => {
  setTimeout(() => {
    resolve(2)
  }, 1000)
})
const promise3 = Promise.resolve(3)
const promise = [promise1, promise2, promise3]
Promise.any(promise).then(res => {
  console.log(res);
})
// 打印结果为 1
const promise1 = Promise.reject(1)
const promise2 = new Promise(resolve => {
  setTimeout(() => {
    resolve(2)
  }, 1000)
})
const promise3 = Promise.reject(3)
const promise = [promise1, promise2, promise3]
Promise.any(promise).then(res => {
  console.log(res);
})
// 打印结果为 2
const promise1 = Promise.reject(1)
const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(2)
  }, 1000)
})
const promise3 = Promise.reject(3)
const promise = [promise1, promise2, promise3]
Promise.any(promise).then(res => {
  console.log(res);
})
// 打印结果为 Uncaught (in promise) AggregateError: All promises were rejected

Promise.resolve()

Promise.resolve()方法是把现有元素转换成状态为fulfilled的Promise实例,

Promise.resolve(1)
// 等价于
new Promise(resolve => resolve(1))

(1)当参数是一个 Promise 实例

如果参数是 Promise 实例,那么Promise.resolve()方法将不做任何修改、原封不动地返回这个实例。

const p = new Promise(resolve => { resolve(1) })
const promise = Promise.resolve(p)
console.log(promise);
/*
打印结果为
Promise {<fulfilled>: 1}
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: 1
*/

(2)当参数是一个thenable对象

如果参数是 thenable对象,Promise.resolve()方法将会把这个对象转换为Promise实例,并立即执行 thenable对象中的then方法。

const thenable = {
  then: function (resolve) {
    resolve(1);
  }
};
const promise = Promise.resolve(thenable)
console.log(promise);
/*
打印结果为
Promise {<pending>}
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: 1
*/

(3)当参数不是thenable对象

当参数不是thenable对象是,Promise.resolve()方法会返回一个新的Promise实例,并且状态为fulfilled。

const p = 1
const promise = Promise.resolve(p)
console.log(promise);
/*
打印结果为
Promise {<fulfilled>: 1}
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: 1
*/

(4)当不带有任何参数

当参数为空时,Promise.resolve()方法会返回一个状态为fulfilled的Promise实例,使用then方法打印res时,结果为undefined。

const promise = Promise.resolve()
console.log(promise);
promise.then((res) => {
  console.log(res);
})
/*
打印结果为
Promise {<fulfilled>: undefined}
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: undefined

undefined
*/

Promise.reject()

Promise.reject()方法也会返回一个新的Promise实例,并且状态为rejected。Promise.reject()方法接收的参数,会作为错误的结果,原封不动的返回,可以在后续的then方法或者catch方法中捕获。

const promise = Promise.reject("报错")
// 等同于
const promise = new Promise((resolve, reject) => { reject("报错") })

console.log(promise);
/*
打印结果为
Promise {<rejected>: '报错'}
  [[Prototype]]: Promise
  [[PromiseState]]: "rejected"
  [[PromiseResult]]: "报错"
*/

手写Promise

// 初始的promise为pending态, resolve后promise转为fullfilled态, reject后转为rejected态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class Promise {
  value = null // 成功的结果
  reason = null // 失败的原因
  state = PENDING // 状态
  // 保存异步的回调
  onFulfilledCallbacks = []
  onRejectedCallbacks = []

  constructor(executor) {
    // 成功的回调
    const resolve = (value) => {
      // 判断是否从初始状态开始改变
      if (this.state === PENDING) {
        // 改变状态
        this.state = FULFILLED
        // 保存成功的结果
        this.value = value
        // 调用then函数的回调函数(发布)
        this.onFulfilledCallbacks.forEach(cb => cb())
      }
    }

    // 失败的回调
    const reject = (reason) => {
      // 判断是否从初始状态开始改变
      if (this.state === PENDING) {
        // 改变状态
        this.state = REJECTED
        // 保存失败的原因
        this.reason = reason
        // 调用then函数的回调函数(发布)
        this.onRejectedCallbacks.forEach(cb => cb())
      }
    }

    // 捕获回调函数中的错误
    try {
      // Promise中的回调函数
      executor(resolve, reject)
    } catch (error) {
      // 报错时改变状态并抛出错误
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    /*
      当then方法不执行resolve或reject函数就执行后面的链式调用时,回调函数则是undefined,会发生报错。
      所以要在入口处添加判断,当reject不是函数时,让它变成函数并返回错误的原因,这样就会被后续的catch捕获到,就做到了异常穿透,
      当resolve不是函数时,需要让它变成一个成功的回调,不需要任何操作,跳过该层即可,当有值传入时就会传出值,这便是值传递,类似异常穿透。
    */
    // 判断回调函数是否为函数
    if (typeof onRejected !== "function") {
      onRejected = reason => {
        throw reason
      }
    }
    if (typeof onFulfilled !== "function") {
      onFulfilled = value => value
    }
    // executor函数成功的回调
    // 链式调用需要返回一个新的Promise
    // 返回的Promise的状态与下一个.then相关
    const promise2 = new Promise((resolve, reject) => {
      if (this.state === FULFILLED) {
        // then方法必须是异步任务,可以是宏任务,也可以是微任务
        queueMicrotask(() => {
          // 当.then报错时,需要在下一个then方法的err提示
          // 使用trycatch捕获错误,发生报错时,手动设置状态为reject并抛出错误
          try {
            const x = onFulfilled(this.value)
            // 对then方法返回的数据进行处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      // executor函数失败的回调
      if (this.state === REJECTED) {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      // 使用发布订阅模式
      // 保存executor函数中异步的结果
      if (this.state === PENDING) {
        // 订阅、记录
        // 在非链式调用的情况下,保存每一个回调函数
        // 用函数的方式进行包裹,方便进行处理
        this.onFulfilledCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })

        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    })

    return promise2
  }

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

  finally(onFinally) {
    return this.then(onFinally, onFinally)
  }

  all(array) {
    // 保存结果
    let result = []
    // 判断是否全部执行
    let index = 0
    return new Promise((resolve, reject) => {
      // 保存结果
      function addData(key, value) {
        // 保存相对应位置的结果
        result[key] = value
        // 增加长度
        index++
        // 如果保存结果的长度和传进来的数组长度一致,则全部完成,返回数组
        if (index === array.length) resolve(result)
      }
      // 遍历数组
      array.forEach((item, index) => {
        // 保存当前项
        let promise = item
        // 判断当前项是否为Promise实例
        if (promise instanceof Promise) {
          promise.then(
            // 成功保存结果
            res => addData(index, res),
            // 失败则直接抛出错误
            rej => reject(rej)
          )
        } else {
          // 如果不是Promise实例直接保存结果
          addData(index, item)
        }
      })
    })
  }

  race(array) {
    return new Promise((resolve, reject) => {
      // 遍历数组
      array.forEach(item => {
        // 保存当前项
        let promise = item
        // 判断当前项是否为Promise实例
        if (promise instanceof Promise) {
          promise.then(
            // 成功返回结果
            res => resolve(res),
            // 失败则直接抛出错误
            rej => reject(rej)
          )
        } else {
          // 如果不是Promise实例直接返回结果
          resolve(item)
        }
      })
    })
  }

  allSettled(array) {
    // 保存结果
    let result = []
    // 判断是否全部执行
    let index = 0
    return new Promise(resolve => {
      // 保存结果
      function addData(key, value, status) {
        // 保存相对应位置的结果
        result[key] = {
          status: status,
          value: value
        }
        // 增加长度
        index++
        // 如果保存结果的长度和传进来的数组长度一致,则全部完成,返回数组
        if (index === array.length) resolve(result)
      }
      // 遍历数组
      array.forEach((item, index) => {
        // 保存当前项
        let promise = item
        // 判断当前项是否为Promise实例
        if (promise instanceof Promise) {
          promise.then(
            // 成功保存结果
            res => addData(index, res, 'fulfilled'),
            // 失败则直接抛出错误
            rej => addData(index, rej, 'rejected')
          )
        } else {
          // 如果不是Promise实例直接保存结果
          addData(index, item, 'fulfilled')
        }
      })
    })
  }

  any(array) {
    // 判断是否全部执行
    let index = 0
    return new Promise(resolve => {
      // 判断失败的次数
      function judgment() {
        index++
        // 如果全部失败,则抛出全部失败
        if (index === array.length) resolve(new Error('All promises were rejected'))
      }
      // 遍历数组
      array.forEach((item, index) => {
        // 保存当前项
        let promise = item
        // 判断当前项是否为Promise实例
        if (promise instanceof Promise) {
          promise.then(
            // 成功返回结果
            res => resolve(res),
            // 失败则保存当前结果
            rej => judgment()
          )
        } else {
          // 如果不是Promise实例直接返回结果
          resolve(item)
        }
      })

    })
  }

}

Promise.resolve = function (value) {
  return new Promise(resolve => {
    // 对Promise实例进行递归处理,如果包含多层Promise实例,则返回最后一层的结果
    function resolvePromise(value) {
      value.then(res => {
        if (res instanceof Promise) {
          resolvePromise(res)
        } else {
          resolve(res)
        }
      })
    }
    // 对thenable对象进行递归处理,如果包含多层thenable对象,则返回最后一层的结果
    function thenablePromise(value) {
      if (value !== null && typeof value === 'object' || typeof value !== 'function') {
        const then = value.then
        // 判断then是否为函数
        if (typeof then === 'function') {
          // 调用then函数,拿到里面的结果
          then.call(
            value,
            res => { thenablePromise(res) }
          )
        } else {
          resolve(value)
        }
      } else {
        resolve(value)
      }
    }
    if (value instanceof Promise) {
      // 判断是否为Promise实例
      resolvePromise(value)
    } else if (value !== null && typeof value === 'object' || typeof value !== 'function') {
      // 判断是否为thenable对象
      thenablePromise(value)
    } else {
      // 如果都不是则直接返回
      resolve(value)
    }
  })
}

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

function resolvePromise(promise2, x, resolve, reject) {
  // If promise and x refer to the same object, reject promise with a TypeError as the reason.
  // 如果promise和x指向同一个对象,则以TypeError为理由拒绝promise。
  if (promise2 === x) {
    reject(new TypeError('Chaining cycle detected for promise'))
  }

  // 判断返回是否为thenable对象
  // promise, { then(){} }, function a(){} a.then = function(){}
  if (x !== null && typeof x === 'object' || typeof x !== 'function') {
    // 在return的thenable对象中的回调函数,可能会报错
    try {
      // return 是thenable对象
      // 按照promise规则走
      const then = x.then
      // { then: 1 }, 可能存在then不是函数的情况,需要进行判断
      if (typeof then === 'function') {
        // 在return的回调中,可能还存在一个thenable对象,需要进行递归处理
        then.call(
          x,
          (res) => resolvePromise(promise2, res, resolve, reject),
          (err) => reject(err)
        )
      } else {
        // then不是函数时,直接抛出返回值
        resolve(x)
      }
    } catch (error) {
      // return的回调报错时,抛出错误
      reject(error)
    }
  } else {
    // return 非thenable对象
    // .then不管是成功的回调还是失败的回调,return的结果一定在下一次.then的成功回调接收
    resolve(x)
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
手写Promise方法是指通过自己编写代码实现Promise类的相关方法,比如Promise.all和Promise.any。在引用和引用中,作者给出了手写Promise.all和Promise.any的具体实现代码。 对于Promise.all方法手写实现,可以使用一个累加器count来判断是否所有的promise都已经成功,并将成功的结果存放在结果数组result中。当累加器count等于传入的promise数组长度时,表示所有的promise都已经成功,可以通过resolve来返回最终的成功结果。如果其中有一个promise失败,则直接通过reject返回失败结果。这里使用了Promise.resolve方法将传入的参数转化为promise对象,以便统一处理。具体的实现代码可以参考引用中的myAll方法。 对于Promise.any方法手写实现,同样可以使用一个累加器count来判断是否已经找到了成功的promise,并将成功的结果通过resolve返回。如果遍历完所有的promise都没有找到成功的,则通过reject返回失败结果。具体的实现代码可以参考引用中的myAny方法。 需要注意的是,上述的手写实现代码可能只是简单的示例,并不一定适用于所有情况,实际的实现可能还需要考虑更多的细节和边界条件。另外,手写Promise方法主要是为了更好地理解Promise的工作原理和实现方式,实际开发中通常会直接使用JavaScript中内置的Promise对象。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [手写promise方法(all、race、allSettled、any、finally),你会几个](https://blog.csdn.net/weixin_45774485/article/details/122462081)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值