首页>>前端>>JavaScript->Promise解析分享

Promise解析分享

时间:2023-12-01 本站 点击:0

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。Promise对象有两个特点,(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。下面是我列的几个Promise的api,也是我们实际开发当中应用频率最高的api,因此结合个人的使用,对这几个api做个分享,以及如何实现一个手写myPromise

Promise.prototype.then()

Promise.prototype.finally()

Promise.all()

Promise.resolve()

手写Promise

then

then方法是Promise实例具有的方法,它的作用是为 Promise 实例添加状态改变时的回调函数,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

`

let p1 = new Promise(function(resolve, reject){    resolve({id: 1, name: 'p1'})})p1.then(res => {    console.log(res) //输出 {id: 1, name: 'p1'}    return {id: 2, name: 'p2'}}).then(res => { // 此处链式调用,上面的return返回的是新的promise实例    console.log(res) // 输出 {id: 2, name: 'p2'}})

`

finally

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作,意思就是状态不管是fulfilled还是rejected,它都必定执行。 finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

`

let p1 = new Promise(function(resolve, reject){    resolve({id: 1, name: 'p1'})})p1.then(res => {    console.log(res)}).finally(() => {    console.log('上面执行完,我也必定执行')})

`

finally最典型的应用场景就是我们的ajax请求的loading关闭,通过这个函数,我们就不需要在ajax请求成功或失败里都去写loading=false了。

all

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

`

let p1 = new Promise(function(resolve){    resolve({id: 1, name: 'p1'})})let p2 = new Promise(function(resolve){    resolve({id: 2, name: 'p2'})})let p3 = {id: 3, name: 'p3'}Promise.all([p1, p2, p3]).then(res => {    console.log(res) // 输出 [{id: 1, name: 'p1'}, {id: 2, name: 'p2'}, {id: 3, name: 'p3'}]})

`

Promise.all的状态由p1、p2、p3决定,分成两种情况。

(1)只有p1、p2、p3的状态都变成fulfilled,Promise.all的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给Promise.all的回调函数,如上案例。

(2)只要p1、p2、p3之中有一个被rejected,Promise.all的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给Promise.all的回调函数。

`

let p1 ...let p2 = new Promise(function(resolve){    throw new Error('报个错')    resolve({id: 2. name: 'p2'})})let p3 ...Promise.all([p1, p2, p3]).then(res => {    // 这里则不在执行}, err => {    // 捕获得错误这里触发    console.log(err) // 输出 error 报个错})

` 这里有个注意事项:p2抛出来的错误,要是被自己这个实例的catch捕获了,那么Promise.all的catch则不会执行,还是会执行fulfilled的状态结果,只是p2这里的返回值会变成undefined,看下面案例

`

let p1 ...let p2 = new Promise(function(resolve){    throw new Error('报个错')    resolve({id: 2. name: 'p2'})}).then(res => res).catch(err => {    console.log(err) // 这里输出错误})let p3 ...Promise.all([p1, p2, p3]).then(res => {    console.log(res) // 输出 [{id: 1, name: 'p1'}, undefined, {id: 3, name: 'p3'}]}, err => {    // 这里则不在触发了})

` 在上面得案例中,p3是个普通对象,把普通对象作为Promise.all数组里的数据传进去,则Promise则会把它处理成Promise的fulfilled状态返回值。

Promise.resolve

有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。在我们上面案例中的p3,在内部转换机制过程中,其实就是调用了这个Promise.resolve()。 Promise.resolve()方法的参数分成四种情况。

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

参数是一个thenable对象,所谓thenable对象,就是指普通对象里面有一个then方法,Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。

`

let thenable = { // 这个就是thenable对象    then: function(resolve, reject){        resolve({id: 1, name: 'thenable'})    }}let p1 = Promise.resolve(thenable);p1.then(function (value) {    console.log(value);  // {id: 1, name: 'thenable'}});

`

参数就是一个普通对象,或者其他数据。如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,并把这个参数作为fulfilled状态的返回值。

啥参数都不传,如果什么参数都么有传,其实就是和第三种情况一样,区别就是它么有传递数据出来。

手写Promise

手写Promise现在网上的教程和案例很多,都是可以值得去学习的,手写Promise在我们求职面试过程中,也是经常遇到面试题,所以这玩意是有必要去学习和掌握的。这里分享一个我学习过程,像这种手写案例你不会的,么得关系,网上找个案例抄,抄一遍你就有概念,抄两遍你就能理解,抄三遍你就能深入了解和学习到人家的思想了,所以,话说到这个份上,要不要掌握和学习就看你自己了,下面我们开始实现我们自己的myPromise。

第一步,我们的Promises是个构造函数,并且promise有三个状态:成功fulfilled,失败rejected,进行中pending, 并且状态间的变化,只能从进行中到成功或者失败,一旦进入这两个状态中的一个,则状态就定型,不在改变。这个构造函数有一个回调函数,这个函数同样还有两个函数参数,一个是接收成功状态的函数,一个失败的,我们代码如下。

`

class myPromise{constructor(callback) {    this.init()    callback(this.resolve.bind(this), this.reject.bind(this))}      init() {    this.state = 'pending'    this.value = null  }resolve(data) {        if (this.state === 'pending') {                this.state = 'fulfilled'                this.value = data        }}reject(err) {        if (this.state === 'pending') {        this.state = 'rejected'        this.value = err        }    }}let mp1 = new myPromise(function(resolve, reject){        resolve({tip: '测试看看状态变化'})        reject({tip: '我会覆盖上面的状态不?'})})console.log(mp1)

`

第二步,添加then方法,then方法是有两个函数参数的,一个是成功的回调,一个是失败的回调,下面我们继续给它添加方法:

`

then(onFulfilled, onRejected) {    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;    onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}        const self = this        return new myPromise(function(resolve, reject){                        if (self.state === 'fulfilled') {            let value = onFulfilled(self.value)            resolve(value)        }        if (self.state === 'rejected') {            let err = onRejected(self.value)            reject(err)            }        })    }mp1.then(res => {    console.log(res) // {tip: '测试看看状态变化'}    return {id: 2, tip: '试试看可以链式调用不'}}).then(res => {    console.log(res) // {id: 2, tip: '试试看可以链式调用不'}})

` 我们的myPromise,实现到这一步,其实基本形态都已经出来了,但是上面的代码还是有问题的,

问题1:我们的错误它不晓得捕获的;

问题2:我们的then方法是一个微任务的异步函数,现在我们的then就是个普通的同步的函数而已;

问题3:我们的then方法是可以在接收异步返回值链式调用的,现在的then只能进行同步函数链式调用。

接下来我们分步骤解决这几个问题:错误捕获,在我们js中用啥处理,不就是try{}catch(){},那安排上吧:变动在于constructor构造函数里的代码块,拿try{}catch(){}包裹:

`

constructor(callback) {   try{    this.init()    callback(this.resolve.bind(this), this.reject.bind(this))    } catch(err) {        this.reject(err)    }}

` 第二个问题是说我们的then方法是个微任务的异步函数,那我们就把异步也加上吧,还有第三个问题是说myPromise接收异步的返回结果不能链式调用的问题,这里我们就一并处理:

`

class myPromise{constructor(callback) {    try{    this.init()    callback(this.resolve.bind(this), this.reject.bind(this))} catch(err) {    this.reject(err)    }}init() {    this.state = 'pending'    this.value = null    this.resolveList = []     this.rejectList = []}resolve(data) {    if (this.state === 'pending') {        this.state = 'fulfilled'        this.value = data        while(this.resolveList.length) {        this.resolveList.shift()(this.value)        }    }}reject(err) {    if (this.state === 'pending') {        this.state = 'rejected'        this.value = err        while(this.rejectList.length) {        this.rejectList.shift()(this.value)        }    }}then(onFulfilled, onRejected) {    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;    onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}    const self = this    return new myPromise(function(resolve, reject){    function microtask (fn) {            // 定义异步处理函数,请注意这个fn,这个fn其实就是then方法的两个回调函数        queueMicrotask(() => { // 这是es6新的api,微任务的一个方法            try{        let result = fn(self.value)        if (result === self) {            throw new Error('不能调用自己')        }         if (result instanceof myPromise) {            result(resolve, reject)            } else {            resolve(result)            }        } catch(err) {            reject(err)            }        })    }    if (self.state === 'fulfilled') {        microtask(onFulfilled)    }    if (self.state === 'rejected') {        microtask(onRejected)    }    if (self.state === 'pending') { // 这里是处理myPromise处理异步返回值            // 之所以异步未能正常处理,就是因为myPromise的状态一直是pending        self.resolveList.push(microtask.bind(self, onFulfilled))        self.rejectList.push(microtask.bind(self, onRejected))        }    })}}let mp1 = new myPromise(function(resolve, reject){setTimeout(() => {        resolve({tip: '测试看看状态变化'})        reject({tip: '我会覆盖上面的状态不?'})}, 1000)            })mp1.then(res => {  console.log(res) // 一秒后输出 {tip: '测试看看状态变化'}return {id: 2, tip: '继续then试试看'}}).then(res => {  console.log(res) // 上一个then输出完以后就到它{id: 2, tip: '继续then试试看'}})

`

实现到这里,我们这个myPromise的then方法已经是完整的了,大家可以自己在浏览器里试试输出打印,这里就不在演示,下面在继续给它增加个all方法。

all方法是Promise里的一个静态方法,静态方法就是由Promise自己调用的,不需要实例话,注意,目前es6支持静态方法,暂时还么有支持静态属性哦。好了下面开始增加all方法,all方法的功能是,接收一个数组参数,参数数组是个promise实例的集合,all方法同样返回一个新的promise实例,这个新实例的fulfilled状态的值就是数组参数所有promise实例状态为fulfilled的值,注意:all方法这个返回的promise实例值,是和数组参数的序列一致的:

`

let p1 = new Promise(function(resolve, reject){    resolve({id: 1, name: 'p1'})})p1.then(res => {    console.log(res)}).finally(() => {    console.log('上面执行完,我也必定执行')})0

` 上面这个方法就是all方法实现的代码了,实现到这个步骤,我们主要实现了myPromise这个类,以及它的两个重要方法,then和all方法。promise的其他方法,就不在赘述了,大家可以自己观察这个方法的功能,看看怎样去实现它们。 今天的Promise解析分享到这里结束。本文重点参考了阮一峰巨佬的 ECMAscript6入门

原文:https://juejin.cn/post/7099746789766463518


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/JavaScript/6425.html