在异步操作里,处理未来可能发生的事情
用途:用来异步计算,将异步操作队列化,按照期望的顺序执行
为什么会有promise ?
javascript包含大量异步操作
异步操作可以避免页面冻结
解决回调地狱问题
以前都是用回调函数处理异步,以下模拟一个场景:
function requestData(url,successCallback,failtrueCallback) {
setTimeout(() => {
// 拿到请求的结果
// url为pp请求成功
if(url ==='pp'){
let name = ['abc','123']
successCallback(name)
}else{ // 否则请求失败
let errMessage = '请求失败'
failtrueCallback(errMessage)
}
}, 3000);
}
requestData('pp',(res)=>{
console.log(res); // ['abc', '123']
},(err)=>{
console.log(err);
})
弊端:
1 如果是我们自己封装requestData,在封装的时候自己设计callback名称
2 用别人封装的request或者第三方库,需要了解别人的代码才能知道这个函数怎么获取结果,使用成本比较高
更好的方案,promise承诺(规范好了代码逻辑)
resolve 参数的三种特殊情况:
1 普通值或对象 pending ---> fulfilled
resolve(123)
}).then(res => {
console.log(res, 'res'); //123
})
2 传入一个promise 状态由传入promise决定,相当于状态转移
function foo(){
return new Promise((resolve, reject) => {
resolve(456)
})
}
const fooPromise = foo();
new Promise((resolve, reject) => {
resolve(fooPromise)
}).then(res => {
console.log(res, 'res'); //456
})
3 传入一个对象,并且这个对象实现了有then 方法
const obj = {
then: function (resolve, reject) {
resolve(789);
}
}
new Promise((resolve, reject) => {
resolve(obj)
}).then(res => {
console.log(res, 'res'); //789
})
promise的对象方法catch:(then catch finally )
无catch前:抛出异常,调用错误回调(.then的第二个参数)可读性差
const promise = new Promise((resolve, reject) => {
//resolve('success')
//reject('error')
throw new Error('报错') //err 也会被捕获
})
promise.then(undefined, err => {
console.log(err, 'err');
})
catch:传入错误捕获的回调函数(上面是catch的语法糖)不符合promise/a+ 规范,但是es6为了代码可读性,给了这种写法
const promise = new Promise((resolve, reject) => {
//resolve('success')
reject('error')
//throw new Error('报错') //err 也会被捕获
})
promise.then(res=>{
return new Promise((resolve, reject) => {
reject('then error')
})
}).catch(err => {
console.log(err, 'err');
})
catch的回调是promise的,不是then后面的new Promise, 但是promise里面没有异常的话,也会捕获new Promise里面的,如果then 和catch 拆开写,那他们是相对独立,互不影响的
catch的返回值和then一样:
promise.then(res=>{
return new Promise((resolve, reject) => {
reject('then error')
})
}).catch(err => {
console.log(err, 'err');
return 'catch'
}).then(res=>{
console.log(res,'res'); // catch res
}).catch(err=>{
console.log(err,'err');
})
finally 是es9新特性,不管变成fulfilled还是rejected状态,最终都会执行finally
promise.then(res=>{
return new Promise((resolve, reject) => {
reject('then error')
})
}).catch(err => {
console.log(err, 'err');
}).finally(() => {
console.log('我是必经之路!');
})
promise类方法:resolve
promise.resolve方法相当于new Promise,并且执行resolve操作
把一个对象转成promise
const promise1 = Promise.resolve({a:'a'})
console.log(promise1,'promise1');
const promise2 = new Promise((resolve, reject) => {
resolve({ a: 'a' })
})
console.log(promise2, 'promise2');
promise类方法:Promise.all ,所有的promise都变成fulfilled再拿到结果, 但是有一个promise变成rejected,那整个promise是rejected ,对于还在处于pending状态的promise,获取不到结果
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(111)
}, 1000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(222)
}, 3000);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(333)
}, 2000);
})
Promise.all([p1, p2, p3]).then((res)=>{
console.log(res,'res');
}).catch((err)=>{
console.log(err,'err'); //err 222
})
但是我不希望,reject阻断我其他的promise,于是乎:es11添加了新的API Promise.allSettled
这个方法会在所有promise都有结果,(无论是fulfilled,还是rejected)才会有最终的形态,且这个promise结果一定是fulfilled,不会走到catch
Promise.allSettled([p1, p2, p3]).then((res)=>{
console.log(res,'res');
}).catch((err)=>{
console.log(err,'err');
})
Promise.race : 有一个promise变成fulfilled就结束
Promise.race([p1, p2, p3]).then((res)=>{
console.log(res,'res'); // res 111
}).catch((err)=>{
console.log(err,'err');
})
如果最先执行rejected,那么就会走catch ,输出 err 222 ,但是我想至少拿到一个结果,那么就会有另外一个方法:
Promise.any : 和race很像,但是至少会返回一个正确结果(无catch)
但是三个请求都是rejected,如图所示
我们还可以通过error,errors拿到里面的数据
简单的实现一个promise:
// 设计 promise
// 保存状态
const PRMOSE_STATUS_PENDING = 'pending'
const PRMOSE_STATUS_FULFILLED = 'fulfilled'
const PRMOSE_STATUS_REJECTED = 'rejected'
class MyPromise {
constructor(excutor) {
this.status = PRMOSE_STATUS_PENDING
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status === PRMOSE_STATUS_PENDING) {
this.status = PRMOSE_STATUS_FULFILLED
setTimeout(() => { // 定时器 宏任务 不会阻塞主线程 (promise是微任务)
console.log('resolve');
this.value = value
// then传进来的回调
this.onFilfilled(value)
}, 0);
}
}
const reject = (reason) => {
if (this.status === PRMOSE_STATUS_PENDING) {
this.status = PRMOSE_STATUS_REJECTED
queueMicrotask(() => {
console.log('reject');
this.reason = reason
this.onRejected(reason)
})
// then传进来的回调
}
}
excutor(resolve, reject) //可以直接调用 promise参数
}
then(onFilfilled, onRejected) {
this.onFilfilled = onFilfilled
this.onRejected = onRejected
}
}
const promise = new MyPromise((resolve, reject) => {
console.log("pending");
resolve(111)
reject(222)
})
// 调用then方法
promise.then(res => {
console.log(res, 'res');
}, err => {
console.log(err, 'err');
})
promise.then(res => { // 调用两次是覆盖,应该并存
console.log(res, 'res');
}, err => {
console.log(err, 'err');
})
目前还不能实现调用两次结果并存,链式调用,按顺序输出
调用两次结果并存
// 设计 promise
// 保存状态
const PRMOSE_STATUS_PENDING = 'pending'
const PRMOSE_STATUS_FULFILLED = 'fulfilled'
const PRMOSE_STATUS_REJECTED = 'rejected'
class MyPromise {
constructor(excutor) {
this.status = PRMOSE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFilfilledFns= []
this.onRejectedFns= []
const resolve = (value) => {
if (this.status === PRMOSE_STATUS_PENDING) {
queueMicrotask(() => { // 定时器 宏任务 不会阻塞主线程 (promise是微任务)
// 执行微任务之前做判断
console.log(this.status,'this.status');
if(this.status !== PRMOSE_STATUS_PENDING) return
this.status = PRMOSE_STATUS_FULFILLED
console.log('resolve');
this.value = value
// then传进来的回调
this.onFilfilledFns.forEach(fn=>{
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PRMOSE_STATUS_PENDING) {
queueMicrotask(() => {
// 执行微任务之前做判断
console.log(this.status, 'this.status2');
if (this.status !== PRMOSE_STATUS_PENDING) return
this.status = PRMOSE_STATUS_REJECTED
console.log('reject');
this.reason = reason
this.onRejectedFns.forEach(fn=>{
fn(this.reason)
})
})
}
}
excutor(resolve, reject) //可以直接调用 promise参数
}
then(onFilfilled, onRejected) { // 定时器的话不会调then 数据塞不到数组里,直接去调用
// 如果then调用的时候,状态已经确认下来了 (包了一层定时器那个情况)
if(this.status == PRMOSE_STATUS_FULFILLED && onFilfilled) {
onFilfilled(this.value)
}
if (this.status == PRMOSE_STATUS_REJECTED && onRejected) {
onRejected(this.reason)
}
// 将成功和失败的回调放在数组里
if(this.status == PRMOSE_STATUS_PENDING){
this.onFilfilledFns.push(onFilfilled)
this.onRejectedFns.push(onRejected)
}
}
}
const promise = new MyPromise((resolve, reject) => {
console.log("pending");
resolve(111)
reject(222)
})
// 调用then方法
promise.then(res => {
console.log(res, 'res');
}, err => {
console.log(err, 'err');
})
promise.then(res => {
console.log(res, 'res');
}, err => {
console.log(err, 'err');
})
// 确定promise状态再调用then
setTimeout(() => {
promise.then(res =>{
console.log(res,'res2');
})
}, 1000);
链式调用
// 保存状态
const PRMOSE_STATUS_PENDING = 'pending'
const PRMOSE_STATUS_FULFILLED = 'fulfilled'
const PRMOSE_STATUS_REJECTED = 'rejected'
class MyPromise {
constructor(excutor) {
this.status = PRMOSE_STATUS_PENDING
this.value = undefined
this.reason = undefined
this.onFilfilledFns= []
this.onRejectedFns= []
const resolve = (value) => {
if (this.status === PRMOSE_STATUS_PENDING) {
queueMicrotask(() => { // 定时器 宏任务 不会阻塞主线程 (promise是微任务)
// 执行微任务之前做判断
console.log(this.status,'this.status');
if(this.status !== PRMOSE_STATUS_PENDING) return
this.status = PRMOSE_STATUS_FULFILLED
console.log('resolve');
this.value = value
// then传进来的回调
this.onFilfilledFns.forEach(fn=>{
fn(this.value)
})
});
}
}
const reject = (reason) => {
if (this.status === PRMOSE_STATUS_PENDING) {
queueMicrotask(() => {
// 执行微任务之前做判断
console.log(this.status, 'this.status2');
if (this.status !== PRMOSE_STATUS_PENDING) return
this.status = PRMOSE_STATUS_REJECTED
console.log('reject');
this.reason = reason
this.onRejectedFns.forEach(fn=>{
fn(this.reason)
})
})
}
}
excutor(resolve, reject) //可以直接调用 promise参数
}
then(onFilfilled, onRejected) { // 定时器的话不会调then 数据塞不到数组里,直接去调用
// 如果then调用的时候,状态已经确认下来了 (包了一层定时器那个情况)
return new MyPromise((resolve, reject) => {
if (this.status == PRMOSE_STATUS_FULFILLED && onFilfilled) {
try {
const value = onFilfilled(this.value)
resolve(value)
} catch (err) { // // 抛出异常才会走reject
reject(err)
}
}
if (this.status == PRMOSE_STATUS_REJECTED && onRejected) {
try {
const value = onRejected(this.value)
resolve(value)
} catch (err) { // 抛出异常才会走reject
reject(err)
}
}
// 将成功和失败的回调放在数组里
if (this.status == PRMOSE_STATUS_PENDING) {
this.onFilfilledFns.push(() => {
try {
const value = onFilfilled(this.value)
resolve(value)
} catch (err) { // 抛出异常才会走reject
reject(err)
}
})
this.onRejectedFns.push(() => {
try {
const value = onRejected(this.value)
resolve(value)
} catch (err) { // 抛出异常才会走reject
reject(err)
}
})
}
})
}
}
const promise = new MyPromise((resolve, reject) => {
// resolve(111)
resolve(222)
})
// 调用then方法多次调用
promise.then(res => {
console.log(res, 'res');
return 'aaa'
}, err => {
return 'bbb'
console.log(err, 'err');
}).then(res => {
console.log(res,'res2');
},err => {
console.log(err,'err2');
})
优化一下,封装工具函数
// 工具函数
function a (exeFns, value, resolve, reject) {
try {
const result = exeFns(value)
resolve(result)
} catch (err) { // 抛出异常才会走reject
reject(err)
}
}
then(onFilfilled, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.status == PRMOSE_STATUS_FULFILLED && onFilfilled) {
a(onFilfilled, this.value, resolve, reject)
}
if (this.status == PRMOSE_STATUS_REJECTED && onRejected) {
a(onRejected, this.value, resolve, reject)
}
// 将成功和失败的回调放在数组里
if (this.status == PRMOSE_STATUS_PENDING) {
this.onFilfilledFns.push(() => {
a(onFilfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
a(onRejected, this.value, resolve, reject)
})
}
})
}
手写promise_catch方法设计
.catch 是 reject 的语法糖
const promise = new Promise((resolve, reject) => {
//resolve('success')
reject('error')
})
promise.then(res=>{
return new Promise((resolve, reject) => {
reject('then error')
})
}).catch(err => {
console.log(err, 'err');
})
正常来说拿到的应该是reject的回调但是 new Promise的回调,需要处理: