一、Promise解决了什么问题
js
function foo(par, successCallback, failCallback) {
// setTimeout用来模拟网络请求
setTimeout(() => {
if(par === 'ywq') {
successCallback({ status: 200, message: '成功获取到数据了' })
} else {
failCallback({ status: -1, message: '数据获取失败' })
}
}, 1000)
}
// 1.传入参数ywq, 结果为res: { status: 200, message: '成功获取到数据了' }
foo('ywq', (res) => {
console.log('res:', res)
}, (err) => {
console.log('err:', err)
})
// 2.传入参数yyy, 结果为err: { status: -1, message: '数据获取失败' }
foo('yyy', (res) => {
console.log('res:', res)
}, (err) => {
console.log('err:', err)
})
上面代码可以拿到获取异步代码的结果,但是我们能看出什么问题呢?
- 需要自己编写命名成功和失败的回调函数
- 如果其他人来使用,需要仔细看代码,判断参数是用来做什么的
总之就是开发成本大,那么Promise就是解决了上面的问题,Promise更加规范的规定这些参数是做什么的,下面就让我们走进Promise的世界吧!
二、Promise的简单使用
js
function foo() {
return '承诺(成功/失败)'
}
let result = foo()
上面是一段伪代码. 一个函数返回给我们一个承诺(这个承诺可能成功,也可能失败),当我们调用这个函数时,我们可以拿到这个承诺的成功与否.
js
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
if(true) {
resolve('我是成功的结果')
}else {
reject('我是失败的结果')
}
}, 300)
})
promise.then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
上面这段代码看不太懂没有关系,我们和下面这段代码结合起来看
js
class Animal {
constructor(executor) {
this.resolve = () => {}
this.reject = () => {}
executor(this.resolve, this.reject)
}
}
const dog = new Animal((resolve, reject) => {
})
- Promise是一个构造函数/类,接收一个参数.这个参数是一个回调函数,一旦被调用,立马会被执行.回调函数还接收两个参数,这两个参数分别又是回调函数
- 在Promise这个回调函数中写异步代码,异步代码取得成功的结果,就调用resolve函数;失败就调用reject函数
- 获取Promise的结果,通过对象的thenable方法获取,then也接收回调函数(可以是一个回调,也可以是两个回调.这里就说是两个,后续再说).分别对应上面的resolve,reject.成功与失败的回调
三、Promise的三种状态(pending, resolved/fulfilled, rejected)
js
new Promise((resolve, reject) => {
console.log('--------') // ①
resolve('111') // ②
reject('222') // ③
resolve('333') // ④
console.log('+++++++++') // ⑤
}).then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
- 在Promise的executor中,Promise的状态为pending(不确定状态)
- 当执行resolve函数时,Promise的状态为resolved/fulfilled(已敲定状态)
- 当执行reject函数时,Promise的状态为rejected(已拒绝状态)
- Promise的状态一旦确定,就不可改变了
调用了resolve/reject函数后,executor后续代码并不影响执行,是因为执行then的时候,resolve/reject的代码才会执行(注意代码执行顺序)
四、Promise的resolve参数
- 当resolve的参数是普通值/对象时
js
new Promise((resolve, reject) => {
// resolve(123) // ① res: 123
// resolve('aaa') // ② res: aaa
// resolve({name: 'ywq', age: 12}) // ③ res: { name: 'ywq', age: 12 }
// resolve(null) // ④ res: null
// resolve() // ⑤ res: undefined
// resolve(Symbol()) // ⑥ res: Symbol()
// resolve(function() {}) // ⑦ res: [Function (anonymous)]
// resolve(['aaa', 'bbb', 'ccc']) // ⑧ res: [ 'aaa', 'bbb', 'ccc' ]
}).then(res => {
console.log('res:', res)
})
- 当resolve的参数是一个Promise时,则当前Promise的状态由传入的Promise的结果决定(状态移交)
js
new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => {
resolve('哈哈哈') // ①
reject('嘿嘿嘿') // ②
}))
}).then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
3.当resolve的参数是一个对象(对象有实现then方法),则会执行这个then方法,并且当前Promise的状态由then方法决定(状态移交)
js
new Promise((resolve, reject) => {
resolve({
then: function(resolve, reject) {
resolve('是我resolve') // ①
reject('是我reject') // ②
}
})
}).then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
五、Promise的对象方法-then
then的参数
js
new Promise((resolve, reject) => {
resolve('我是正确的') // ①
reject('我是错误的') // ②
}).then((res) => {
console.log('我是成功时被执行了', res)
}, (err) => {
console.log('我是失败时被执行了', err)
})
-
then方法可以接收两个回调函数
第一个回调函数是执行resolve时被回调,可以接收参数,参数是resolve的结果
第二个回调函数是执行reject时被回调,可以接收参数,参数是rejected的结果
js
const promise = new Promise((resolve, reject) => {
reject('太丑了')
})
promise.then(res => {
console.log('哈哈,我被接受了')
})
promise.catch(err => {
console.log(`额,我被拒绝了,原因是${err}`)
})
-
then方法可以接受一个回调函数(成功捕获和错误捕获可以分开写)
成功捕获使用 then
错误捕获使用 catch
在node环境中打印会报错(This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch()),原因是因为我们通过then和catch分别去获取结果(互不相干),当Promise异步代码发生异常,then方法并没有去捕获错误
then方法可以被多次调用
js
const promise = new Promise((resolve, reject) => {
resolve('呵呵呵呵呵')
})
promise.then(res => { console.log('res1:', res) }) // ① res1: 呵呵呵呵呵
promise.then(res => { console.log('res2:', res) }) // ② res2: 呵呵呵呵呵
promise.then(res => { console.log('res3:', rves) }) // ③ res3: 呵呵呵呵呵
- resolve方法被回调时,所有的then方法传入的回调函数都会被调用
六、 Promise的链式调用/then方法可以有返回值
js
new Promise((resolve, reject) => {
resolve('a')
}).then(res => {
console.log('获取了第一个的正确结果:', res)
return 'b' // ①
return new Promise((resolve, reject) => {
setTimeout(() => { reject(333) }, 300)
}) // ②
return { then: function(resolve, reject) { reject(444) } } // ③
}, err => {
console.log('获取了第一个的错误结果:', err)
}).then(res => {
console.log('获取了第二个的正确结果:', res)
}, err => {
console.log('获取了第二个的错误结果:', err)
})
- then方法的回调函数可以有返回值,返回值会被作为一个新的Promise
- 返回普通的值/对象 普通的值/对象会被作为新的Promise的resolve值
- 返回一个新的Promise
- 返回一个实现thenable方法的对象
七、Promise的对象方法-catch
上面我们讲了promise的两种错误捕捉catch
- then方法的第二个回调函数
- Promise对象的catch方法单独调用
第三种
- 用于Promise的链式调用的尾部
js
new Promise((resolve, reject) => {
resolve('正确111') // ①
reject('错误111') // ②
}).then(res => {
return new Promise((resolve, reject) => { resolve('正确222') }) // ③
return new Promise((resolve, reject) => { reject('错误222') }) // ④
}).then(res => {
return { name: 'ywq' }
}).catch(err => {
console.log('err:', err)
})
- catch是依次捕捉Promise的错误结果.第一个Promise如果错误,就可以打印catch的错误结果;如果正确,会捕捉下一个Promise的错误...依次类推
js
new Promise((resolve, reject) => {
reject('err1') // ① err: err1
throw new Error('err2') // ② err: Error: err2
}).then(res => {}, err => {
console.log('err:', err)
})
- 当executor中的代码出现异常错误,也会被捕获到
then的错误捕获也可以有返回值,返回值也是一个新的Promise,同then的成功捕获一样
js
new Promise((resolve, reject) => {
reject('a')
}).then(() => {}, err => {
console.log('获取了第一个的错误结果:', err)
return '错误b' // ①
return new Promise((resolve, reject) => { resolve('正确c') }) // ②
return new Promise((resolve, reject) => { reject('错误c') }) // ③
return ({ then: function(resolve, reject) { resolve('正确d') }}) // ④
return ({ then: function(resolve, reject) { reject('错误d') }}) // ⑤
}).then(res => {
console.log('获取了第二个的正确结果:', res)
}, err => {
console.log('获取了第二个的错误结果:', err)
})
八、Promise的对象方法-finally
js
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success') // ①
reject('fail') // ②
}, 300)
}).then(res => {
console.log('res:', res)
}).catch(err => {
console.log('err:', err)
}).finally(() => {
console.log('finally的回调函数被执行了')
})
- finally也接收一个回调函数.这个回调函数没有参数,不管Promise的状态是resolved/rejected,最终都会被执行.这个回调函数主要用来做清除工作.
九、Promise的6个类方法
Promise.resolve
js
// 将一个对象变为一个Promise (pending => resolved)
new Promise((resolve) => { resolve({ name: 'ywq' }) })
.then(res => { console.log('res:', res) })
// 上面代码等同于
Promise.resolve({ name: 'ywq' })
.then(res => { console.log('res:', res) })
js
// 测试
Promise.resolve(123)
.then(res => { console.log('res:', res) }) // ① res: 123
Promise.resolve({then: function(resolve) { resolve('哈哈哈') }})
.then(res => { console.log('res:', res) }) // ② res: 哈哈哈
Promise.resolve(new Promise((resolve) => resolve({ name: 'ywq' })))
.then(res => { console.log('res:', res) }) // ③ res: { name: 'ywq' }
- 如果明确将"某个值/对象/对象实现了thenable方法/新的Promise"转变为Promise已敲定状态,可以直接使用Promise.resolve()
Promise.reject
js
// 将一个对象变为一个Promise (pending => rejected)
new Promise((resolve, reject) => { reject({ name: 'ywq' }) })
.then(res => { })
.catch(err => { console.log('err:', err) })
// 上面代码等同于
Promise.reject({ name: 'ywq' })
.then(() => {})
.catch(err => { console.log('err:', err) })
js
// 测试
Promise.reject(123)
.then(res => { console.log('res:', res) })
.catch(err => { console.log('err:', err) }) // ① err: 123
Promise.reject({then: function(resolve) { resolve('哈哈哈') }})
.then(res => { console.log('res:', res) })
.catch(err => { console.log('err:', err) }) // ② err: { then: [Function: then] }
Promise.reject(new Promise((resolve) => resolve({ name: 'ywq' })))
.then(res => { console.log('res:', res) })
.catch(err => { console.log('err:', err) }) // ③ err: Promise { { name: 'ywq' } }
- 如果明确转变为Promise已拒绝状态,"传入任何值都是已拒绝状态",可以直接使用Promise.reject() !!!特别注意
这是一段公共代码
js
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise1_resolved') // ①
reject('promise1_rejected') // ②
}, 1000)
})
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise2_resolved') // ③
reject('promise2_rejected') // ④
}, 2000)
})
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise3_resolved') // ⑤
reject('promise3_rejected') // ⑥
}, 3000)
})
Promise.all
js
const arr = [ promise1, promise2, promise3, 123, { name: 'ywq' } ]
Promise.all(arr).then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
- Promise.all接收一个数组,数组里面的参数都会转变为Promise,按传入参数的顺序,依次执行每个Promise
- 当所有Promise的状态为resolved时,返回一个数组
- 当只要有一个Promise的状态为rejected时,整个Promise数组的状态就为rejected,并立即暂停执行后续每个Promise
Promise.allSettled
js
const arr = [ promise1, promise2, promise3, 123, { name: 'ywq' } ]
Promise.allSettled(arr).then(res => {
console.log('res:', res)
})
-
Promise.allSettled接收一个数组,数组里面的参数都会转变为Promise,按传入参数的顺序,依次执行每个Promise
-
每个Promise都会被执行,不管状态是否为rejected/fulfilled.永远不会执行catch
返回结果eg: [{ status: 'rejected', reason: 'xxxx' }, { status: 'fulfilled', value: 'xxxx' }]
Promise.race
js
const arr = [ promise1, promise2, promise3 ]
Promise.race(arr).then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
- Promise.allSettled接收一个数组,数组里面的参数都会转变为Promise,按传入参数的顺序,依次执行每个Promise
- 只要pending状态最先被改变了,立马结束. 结果为最先得到结果的那个Promise(不管Promise是fulfilled状态还是rejected状态)
Promise.any
js
const arr = [ promise1, promise2, promise3 ]
Promise.any(arr).then(res => {
console.log('res:', res)
}, err => {
console.log('err:', err)
})
- Promise.any接收一个数组,数组里面的参数都会转变为Promise,按传入参数的顺序,依次执行每个Promise
- 结果为最先返回状态为fulfilled的那个Promise.
- 如果所有的Promise都为rejected状态,结果为eg: [AggregateError: All promises were rejected] { [errors]: [ 'xxx', 'xxx', 'xxx' ] }