Promise 之前实现异步的弊端
首先看一个例子,再没有Promise之前,实现一个网络异步请求:
function requestData(url, successCallback, failtureCallback) {
// 模拟网络请求
setTimeout(() => {
// 获取请求结果
// url 传入的是 ricky 则请求成功,否则请求失败
if (url === 'ricky') {
let res = {
name: 'ricky',
age: 18
};
successCallback(res);
} else {
let error = '请求失败';
failtureCallback(error);
}
}, 3000);
}
requestData('ricky', (res) => {
console.log(res);
} , (error) => {
console.log(error);
} );
这种实现方法有很多弊端:
- 如果是我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称, 并且使用好
- 如果我们使用的是别人封装的requestData或者一些第三方库, 那么我们必须去看别人的源码或者文档, 才知道它这个函数需要怎么去获取到结果
什么是 Promise
当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;
在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor
- 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject;
- 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
- 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
通过Promise改进的异步请求方法:
function requestData(url) {
// 模拟网络请求
return new Promise((resolve, reject) => {
setTimeout(() => {
// 获取请求结果
// url 传入的是 ricky 则请求成功,否则请求失败
if (url === 'ricky') {
let res = {
name: 'ricky',
age: 18
};
resolve(res);
} else {
let error = '请求失败';
reject(error);
}
}, 3000);
});
}
requestData('ricky').then((res) =>
console.log(res), (err) =>
console.log(err)
);
Promise 三种状态
- pending: 初始状态,表示Promise对象正在处理状态,还未完成
- fulfilled: 表示Promise对象已经成功处理,当执行resolve方法时,Promise对象的状态就会变为fulfilled
- rejected: 表示Promise对象已经处理失败,当执行reject方法时,Promise对象的状态就会变为rejected
const promise = new Promise((resolve, reject) => {
// 调用resolve方法, then 传入回调执行
resolve('successCallback')
// 调用reject方法, catch 传入回调执行
reject('errorCallback')
})
resolve 传参区别
- 如果resolve传入一个普通的值或者对象,那么这个值会作为then回调的参数;
- 如果resolve传入一个Promise对象,那么这个Promise对象的状态就会被设置为当前Promise对象的状态;
- 如果resolve传入一个有then方法的对象,那么这个对象的then方法就会被调用,并且传入的参数就是当前Promise对象的状态;
// 传入Promise对象
const newPromise = new Promise((resolve, reject) => {
reject("err message")
})
new Promise((resolve, reject) => {
// pending -> fulfilled
resolve(newPromise)
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err) // err: err message
})
// 传入有then方法的对象
new Promise((resolve, reject) => {
// pending -> fulfilled
const obj = {
then: function(resolve, reject) {
// resolve("resolve message")
reject("reject message")
}
}
resolve(obj)
}).then(res => {
console.log("res:", res)
}, err => {
console.log("err:", err) // err: reject message
})
then 方法
then是Promise对象上的方法,在Promise原型上的Promise.prototype.then
两个参数
then方法接受两个参数,分别是resolve和reject,当Promise对象的状态变为fulfilled时,会执行resolve,当Promise对象的状态变为rejected时,会执行reject
promise.then(res => {
console.log(res);
}, err => {
console.log(err);
})
// 等价于
promise.then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
多次调用
Promise对象的then方法可以被多次调用,每次调用我们都可以传入对应的fulfilled回调;当Promise的状态变成fulfilled的时候,这些回调函数都会被执行;
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000);
})
promise.then(res1 => {
console.log('res1:', res1);
})
promise.then(res2 => {
console.log('res2:', res2);
})
promise.then(res3 => {
console.log('res3:', res3);
})
// 上述三个结果都在同一时间打印
then方法的返回值
then方法的返回值是一个新的Promise对象,这个新的Promise对象的状态和原来的Promise对象保持一致。
// resolve情况
promise.then(res => {
console.log('res:', res); // res: success
return 'res success'
}).then(res => console.log(res), err => console.log(err)) // res success
// reject情况
promise.then(res => {
console.log('res:', res); // res: success
throw 'res err'
}).then(res => console.log(res), err => console.log(err)) // res err
catch 方法
catch方法是then方法的另一个版本,用于指定rejected的回调函数,当Promise对象的状态变为rejected时,会执行rejected
多次调用
Promise对象的catch也可以进行多次调用,每次调用都会执行对应的回调函数;当Promise的状态变成rejected的时候,这些回调函数都会被执行;
// 同样会在promise对象的状态变为rejected的时候同时执行
promise.catch(err => console.log("err1:" err))
promise.catch(err => console.log("err2:" err))
catch返回值
catch方法的返回值也是一个新的Promise对象,这个新的Promise对象的状态和原来的Promise对象保持一致。
promise.catch(err => {
console.log('err:', err); // err: err message
return err
}).then(res => {
console.log('res:', res); // 执行此处
}).catch(err => {
console.log(err);
})
promise.catch(err => {
console.log('err:', err); // err: err message
throw err
}).then(res => {
console.log('res:', res);
}).catch(err => {
console.log(err); // 执行此处
})
finally 方法
finally方法是then方法的另一个版本,用于指定无论Promise对象最后状态如何,都会执行的回调函数。finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行。
promise.then(res => {
console.log('res:', res); // res: success
}).catch(err => {
console.log('err:', err);
}).finally(() => {
console.log('finally'); // finally
})
resolve & reject 方法
有时候我们已经有一个现成的内容了,希望将其转成Promise来使用,这个时候我们可以使用 Promise.resolve/Promise.reject 方法来完成。
Promise.resolve的用法相当于new Promise,并且执行resolve操作,可以传递除了普通值或对象、Promise和thenable对象作为参数。
Promise.reject的用法也相当于new Promise,并且执行reject操作,可以传递任何形态的参数,这些参数都会直接作为reject状态的参数传递到catch。
Promise.resolve('success')
// 相当于
new Promise((resolve, reject) => {
resolve('success')
})
Promise.reject('err')
// 相当于
new Promise((resolve, reject) => {
reject('err')
})
Promise.all方法
Promise.all方法用于将多个Promise对象,包装成一个新的Promise对象。
新的Promise状态由包裹的所有Promise共同决定:
- 当所有的Promise状态变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值组成一个数组;
- 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数;
const r1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('r1 success')
}, 1000);
})
const r2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('r2 success')
}, 1500);
})
const r3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('r3 success')
}, 2000);
})
const e1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('e1 error')
}, 1000);
})
const e2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('e2 error')
}, 2000);
})
Promise.all([r1, r2, r3]).then(res => {
console.log(res); // [ 'r1 success', 'r2 success', 'r3 success' ]
})
Promise.all([r1, r2, r3, e1, e2]).then(res => {
console.log(res);
}).catch(err => {
console.log(err); // e1 error
})
Promise.allSettled 方法
all方法有一个缺点:如果一个Promise变成reject状态,那么其他的Promise也会变成reject状态,并且不会再执行then方法。无法获取已经resolved状态的Promise。ES11中新增了allSettled方法,用于解决这个问题。该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,都有最终的状态,并且这个Promise的结果一定是fulfilled的。
返回的Promise的结果是一个数组,数组中的每一项都是一个Promise的结果,包含status状态以及对应的value值,数组的顺序与传入的Promise数组的顺序一致。
Promise.allSettled([r1, r2, r3, e1, e2]).then(res => {
console.log(res); // [ { status: 'fulfilled', value: 'r1 success' }, { status: 'fulfilled', value: 'r2 success' }, { status: 'fulfilled', value: 'r3 success' }, { status: 'rejected', reason: 'e1 error' }, { status: 'rejected', reason: 'e2 error' } ]
})
Promise.race方法
如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法:
Promise.race([r1, r2, r3]).then(res => {
console.log(res); // r1 success
})
Promise.any方法
any方法是ES12中新增的方法,和race方法是类似的:
- any方法会等到一个fulfilled状态,才会决定新Promise的状态
- 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态
Promise.any([r1, r2, r3, e1, e2]).then(res => {
console.log(res); // 'r1 success'
})
Promise.any([e1, e2]).then(res => {
console.log(res);
})
// 报错:Error: All promises were rejected