【超简单】一文搞懂Promise的使用

一、Promise的基本使用

Promise传入回调函数 ,一传入就立即执行,传入的这个函数被称为executor,回调函数里面又有两个回调函数。一个成功的回调,一个失败的回调。

js 复制代码
// resolve: 回调函数, 在成功时, 回调resolve函数
// reject: 回调函数, 在失败时, 回调reject函数
const p = new Promise((resolve, reject) => {
    console.log('立即执行')
    // resolve('成功的回调')
    reject('失败的回调')
})
js 复制代码
// then方法可以传入两个回调函数,用于接收Promise的执行结果
// 第一个会在Promise执行resolve函数时获取成功的回调,第二个会在Promise执行reject函数时获取失败的回调。
p.then(res => {
    console.log(res)
}, err => {
    console.log(err) // 输出'失败的回调'
})

// catch方法传入的回调函数, 会在Promise执行reject函数时, 被回调
p.then(res => {
    console.log(res)
}).catch(err => {
    console.log(err) // 输出'失败的回调'
})

// 如果是这样,catch将会成为第二个Promise对象失败的回调。(因为then方法也有返回值,是一个promise对象)具体可看下面then方法的使用
p.then(res => {
    console.log(res)
}, err => {
    console.log(err) // 输出'失败的回调'
    return Promise.reject('第二个promise失败回调')
}).catch(err => {
    console.log(err) // 输出'第二个promise失败回调'
})

注意:当 Promise 对象什么都不传时,执行会报错,规定必须给 Promise 对象传入一个执行函数,否则将会报错。

js 复制代码
var promise = new Promise() // 报错

二、Promise的使用场景

Promise 对象代表一个异步操作,将异步操作以同步的流程表达出来,为了解决异步处理造成的回调地狱问题而产生的。在真实的开发场景中,数据的请求一般是异步的,且时间是不定的,我们可以模拟这样的场景。

js 复制代码
function fn1() {
    setTimeout(() => {
        console.log('我')
    }, 300)
}

function fn2() {
    setTimeout(() => {
        console.log('喜欢')
    }, 200)
}

function fn3() {
    setTimeout(() => {
        console.log('你')
    }, 100)
}

// 为了得到正确的返回数据,我们必须这样做
setTimeout(function () {  //第一层
    fn1()
    setTimeout(function () {  //第二程
        fn2()
        setTimeout(function () {   //第三层
            fn3()
        }, 100)
    }, 200)
}, 300)
// 依次输出我喜欢你

代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。这样代码的阅读性不好,而且定时器的时间还要随着数据返回的时间改变而改变(可以把fn1的时间变为1000试试),才能保证数据的正确返回顺序。

为了解决上述问题,我们可以让接口返回一个promise对象

js 复制代码
function fn1() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('我')
        }, 1000)
    })
}

function fn2() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('喜欢')
        }, 200)
    })
}

function fn3() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('你')
        }, 100)
    })
}

fn1().then(res => {
    console.log(res)
    return fn2()
}).then(res => {
    console.log(res)
    return fn3()
}).then(res => {
    console.log(res)
})

// 1s后输出'我', 再过200ms后输出'喜欢',再过100ms后输出'你'

这样不仅解决了回调地狱的问题,也能保证即使fn1的时间即使更改了,数据的执行顺序也能正确返回(具体原因,可了解一下js的执行机制。)

三、Promise有三种状态:

已成功 (Fulfilled) : 执行成功。

已拒绝 (Rejected) : 执行失败。

待定 (Pending) : 可以理解为Promise对象实列创建时候的初始状态,既没成功,也没拒绝。

Promise 一旦状态改变,就不会再变 ,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected 。

js 复制代码
new Promise((resolve, reject) => {
    // pending状态: 待定/悬而未决的
    console.log('立即执行') // 输出'立即执行'
    resolve('成功的回调') // 处于fulfilled状态(已敲定/兑现状态)
    // 前面的状态已经更改,这里不会执行了
    reject('失败的回调') // 处于rejected状态(已拒绝状态)
    console.log('这里依然可以继续执行') // 输出'这里依然可以继续执行'
}).then(res => {
    console.log('====', res) // 输出'成功的回调'
}).catch(err => {
    console.log('===', err) // 状态已经改变了,这里不会输出了
})
// 注意: Promise状态一旦确定下来, 那么就是不可更改的(锁定)

四、Promise的resolve参数

resolve的参数,可以传入的值:

1、一个普通的对象 --> pending -> fulfilled

js 复制代码
new Promise((resolve, reject) => {
    resolve('aaa') // pending -> fulfilled
}).then(res => {
    console.log(res) // 输出'aaa'
}).catch(err => {
    console.log(err) 
})

2、传入一个promise --> 如果传入的是一个promise,那么当前的Promise的状态会由传入的Promise来决定,相当于状态进行了移交。

js 复制代码
const myPromise = new Promise((resolve, reject) => {
	reject('我发生了失败的转移')
})

new Promise((resolve, reject) => {
    resolve(myPromise) // pending -> rejected
}).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err) // 输出'我发生了失败的转移'
})

3、传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口),那么也会执行该then方法, 并且又该then方法决定后续状态,也相当于状态进行了移交。

js 复制代码
new Promise((resolve, reject) => {
    const obj = {
        then: function(resolve, reject) {
            resolve('对象的then方法,自动执行了')
        }
    }
    resolve(obj) // 传入一个带有then方法的对象
}).then(res => {
    console.log(res) // 输出'对象的then方法,自动执行了'
}).catch(err => {
    console.log(err) 
})

五、Promise对象的方法

在了解Promise对象的方法之前我们可以先使用Object.getOwnPropertyDescriptors()方法看看Promise的原型上有哪些属性和方法

js 复制代码
// Object.getOwnPropertyDescriptors 是 JavaScript 中的一个方法,它返回指定对象所有自身属性的描述对象
console.log(Object.getOwnPropertyDescriptors(Promise.prototype))


// 输出
// { 
//     constructor: {value: [Function: Promise],writable: true,enumerable: false,configurable: true},
//     then: { value: [Function: then],writable: true,enumerable: false,configurable: true},
//     catch: {value: [Function: catch],writable: true,enumerable: false,configurable: true},
//     finally: {value: [Function: finally],writable: true,enumerable: false,configurable: true},
//     [Symbol(Symbol.toStringTag)]: {value: 'Promise',writable: false,enumerable: false,configurable: true}
// }
// 说明由Promise构造的对象可以继承原型上的then,catch,finally等方法
1、Promise对象上的then方法

1.1、同一个Promise可以被多次调用then方法

当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用

js 复制代码
const myPromise =  new Promise((resolve, reject) => {
	resolve('成功的回调')
})
myPromise.then(res => {
	console.log('res1:', res) // 输出'成功的回调'
})
myPromise.then(res => {
	console.log('res2:', res) // 输出'成功的回调'
})
myPromise.then(res => {
	console.log('res3:', res) // 输出'成功的回调'
})

1.2、then方法本身也是有返回值的, 它的返回值是Promise

1.2.1、如果我们返回的是一个普通值(数值/字符串/普通对象/undefined),那么这个普通的值被作为一个新的Promise的resolve值

js 复制代码
const myPromise =  new Promise((resolve, reject) => {
    resolve('成功的回调')
})

myPromise.then(res => {
    console.log(res)
    // return {'name': 'wbb'}
    // return 1111
    // return 'bbb'
    return
}).then(res => {
    console.log(res) // 输出'undefined'
})

1.2.2、如果我们返回的是一个Promise,会对状态进行移交。

js 复制代码
myPromise.then(res => {
    return new Promise((resolve, reject) => {
        reject('失败了')
    })
}).then(res => {
    console.log(res)
}, err => {
    console.log(err) // 输出'失败了'
})

1.2.3、如果返回的是一个对象, 并且该对象实现了thenable,会对状态进行移交。

js 复制代码
myPromise.then(res => {
    return {
        then: function(resolve, reject) {
            resolve(222222)
        }
    }
}).then(res => {
    console.log("res:", res) // 输出222222
})
2、Promise对象上的catch方法

2.1、通过catch方法来传入错误(拒绝)捕获的回调函数

js 复制代码
const promise = new Promise((resolve, reject) => {
    reject('哈哈')
})
promise.then(res => {
    console.log(res)
}).catch(err => {
    console.log(err) // 输出'哈哈'
})

2.2、当executor抛出异常时, 会调用错误(拒绝)捕获的回调函数

js 复制代码
const promise = new Promise((resolve, reject) => {
    throw new Error('手动抛出异常')
})
promise.then(res => {
    console.log(res)
}).catch(err => {
    console.log(err) // 输出'手动抛出异常'
})

2.3、catch方法的返回值。同上面then方法的返回值

js 复制代码
const promise = new Promise((resolve, reject) => {
    reject('233')
})
promise.then(res => {
    console.log("res:", res) 
    return '12345'
}).catch(err => {  
    console.log("err:", err) // 输出'res: 233'
    return "catch return value"  // 同样是返回一个promise
}).then(res => {
    console.log("res result:", res) // 输出'catch return value'
}).catch(err => {
    console.log("err result:", err)
})
3、Promise对象上的finally方法

finally:表示Promise对象无论变成fulfilled还是rejected,最终都会执行的代码

finally方法是不接收参数的,因为前面无论是fulfilled还是rejected状态,都会执行

js 复制代码
const promise = new Promise((resolve, reject) => {
    resolve('成功的回调')
})

promise.then(res => {
    console.log("res:", res) // 输出'res: 成功的回调'
    return 111
}).catch(err => {
    console.log("err:", err)
}).finally(() => {
    console.log("finally code execute") // 输出'finally code execute'
    return 222
}).then(res1 => {
    console.log("res1:", res1) // 输出111
})

六、Promise类的方法

1、Promise.resolve()和Promise.reject()方法

可传入的值等同于resolve的参数,可以传入的值。

js 复制代码
function foo() {
    const obj = {name: 'wbb'}
    let flag = true
    return new Promise(resolve => {
        if(flag) {
            resolve(obj)
        } else {
            reject('报错了')
        }
    })
}

// 等同于
function foo() {
    const obj = {name: 'wbb'}
    let flag = true
    if(flag) {
        return Promise.resolve(obj)
    } else {
        return Promise.reject('报错了')
    }
}
2、Promise.all()方法

all本身返回的是一个promise

按照传入的顺序返回

js 复制代码
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('111')
    }, 1000)
})

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('222')
    }, 2000)
})

const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(333)
    }, 3000)
})

const p4 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('444')
    }, 4000)
})

const p5 = new Promise((resolve, reject) => {
    setTimeout(() => {
        
    }, 2000)
})

const p6 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('666')
    }, 2000)
})

const p7 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('777')
    }, 2000)
})

// 1、所有的状态都为fulfilled状态
Promise.all([p2, p1, p3, 'aaa', p4]).then(res => {
    console.log(res)
}).catch(err => {
    console.log('err', err)
})
// 4秒后按照顺序返回 ['222', '333', 'aaa', '111', '444']

// 2、Promise有一个状态为pending
Promise.all([p2, p1, p5]).then(res => {
    console.log(res)
}).catch(err => {
    console.log('err', err)
})
// 不会输出,all得等到所有的Promise的所有状态都为fulfilled,或者至少有一个状态为rejected才会输出

// 3、存在reject状态,不管Promise有没有一个状态为pending,都会输出
Promise.all([p2, p4, p5, p6, p7]).then(res => {
    console.log(res)
}).catch(err => {
    console.log('err', err)
})
// 2s后按照顺序输出错误信息['666', '777']
3、Promise.allSettled()方法

希望所有东西都有最终的结果,依次返回状态和值

不会走catch,以一个对象返回在then方法返回

js 复制代码
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('1111')
    }, 1000)
})

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('2222')
    }, 2000)
})

const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('333')
    }, 3000)
})

const p4 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('1111')
    }, 4000)
})

const p5 = new Promise((resolve, reject) => {
    setTimeout(() => {
        
    }, 2000)
})

// 1、所有的Promise状态都已经改变
Promise.allSettled([p2, p1, p3, 'aaa', p4]).then(res => {
    console.log(res)
}).catch(err => {
    console.log('err', err)
})  // 不会走catch,以一个对象返回在then方法返回

// 4秒后按照存入顺序返回
// [
//     { status: 'fulfilled', value: '2222' },
//     { status: 'fulfilled', value: '1111' },
//     { status: 'rejected', reason: '333' },
//     { status: 'fulfilled', value: 'aaa' },
//     { status: 'fulfilled', value: '1111' }
// ]

// 2、Promise有一个状态为Pending
Promise.allSettled([p2, p1, p3, 'aaa', p5]).then(res => {
    console.log(res)
})
// 不会返回结果
4、Promise.race()方法

只要有一个promise有结果,就结束。可以理解为赛跑,谁先有结果谁先输出。只输出一个结果。

js 复制代码
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('1111')
    }, 1000)
})

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('2222')
    }, 2000)
})

const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
    }, 3000)
})

const p4 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('1111')
    }, 4000)
})

// 只有有一个promise有结果,就结束
// 'aaa'是最快的
Promise.race([p2, p1, p3, 'aaa', p4]).then(res => {
    console.log(res) // 立即输出'aaa'
}).catch(err => {
    console.log('err', err)
})

Promise.race([p2, p1, p3, p4]).then(res => {
    console.log(res)
}).catch(err => {
    console.log('err', err)  // 1秒后输出'err 1111'
}) 
5、Promise.any()方法

any要等到至少有一个fulfilled,才有对应的结果

js 复制代码
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('1111')
    }, 1000)
})

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('2222')
    }, 500)
})

const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('3333')
    }, 3000)
})

const p4 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('4444')
    }, 4000)
})


// any要等到至少有一个fulfilled,才有对应的结果
Promise.any([p1, p3, p4, p2]).then(res => {
    console.log(res)
}).catch(err => {
    console.log('err', err)
})

// 500ms后输出'2222'

// 如果全是拒绝呢?
// 等到所有的拒绝全部执行完,才有结果.
// 输出: 'err AggregateError: All promises were rejected'
相关推荐
会发光的猪。21 分钟前
vue中el-select选择框带搜索和输入,根据用户输入的值显示下拉列表
前端·javascript·vue.js·elementui
旺旺大力包38 分钟前
【 Git 】git 的安装和使用
前端·笔记·git
雪落满地香1 小时前
前端:改变鼠标点击物体的颜色
前端
余生H1 小时前
前端Python应用指南(二)深入Flask:理解Flask的应用结构与模块化设计
前端·后端·python·flask·全栈
outstanding木槿2 小时前
JS中for循环里的ajax请求不数据
前端·javascript·react.js·ajax
酥饼~2 小时前
html固定头和第一列简单例子
前端·javascript·html
一只不会编程的猫2 小时前
高德地图自定义折线矢量图形
前端·vue.js·vue
m0_748250932 小时前
html 通用错误页面
前端·html
来吧~2 小时前
vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)
前端·vue.js·音视频
han_2 小时前
不是哥们,我的console.log突然打印不出东西了!
前端·javascript·chrome