Promise
是JavaScript在ES6中引入的一种用于异步计算的对象。它代表了一个可能现在还没有结果,但将来某个时刻会有结果的值。Promise
的主要目的是提供一个更合理、更强大的方式来处理异步操作。
状态:一个 Promise
对象有三种状态:
Pending(等待中):初始状态,既不是成功,也不是失败状态。
Fulfilled(已成功):意味着操作成功完成。
Rejected(已失败):意味着操作失败。
创建:使用 new Promise
构造函数来创建一个新的 Promise
对象。
let promise = new Promise((resolve, reject) => {
if(/*条件成立*/) {
resolve() // 调用成功方法,状态改变为fulfilled
} else {
reject() // 调用失败方法,状态改变为rejected
}
}
promise构造函数接受一个回调函数作为参数,回到函数会被注入resolve与reject两个函数作为参数,分别用于改变promise对象的两种状态。
值得注意的是,一但promise的状态由Pending切换为Fulfilled或Rejected之一后,将无法再次改变。同时promise的状态只有通过其构造函数来改变,在promise外部无法修改其状态。
3. 处理结果:使用 .then()
方法来添加在 Promise 成功时执行的回调函数,使用 .catch()
方法来添加在 Promise 失败时执行的回调函数。
promise .then(value => {
// 当 promise 成功时,这里的代码会被执行
console.log('Success:', value);
}) .catch(error => {
// 当 promise 失败时,这里的代码会被执行
console.error('Error:', error);
});
4.链式调用:.then()
方法返回一个新的 Promise
,这允许你链式调用多个 .then()
和 .catch()。
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.catch(error => console.error(error));
5. Promise.all():用于处理多个 Promise 对象,只有当所有 Promise 都成功时,它才会成功。如果任何一个 Promise 失败,它就会失败。
Promise.all([promise1, promise2, promise3])
.then(([result1, result2, result3]) => {
// 所有 Promise 都成功时执行
}) .catch(error => {
// 任何一个 Promise 失败时执行
});
6. Promise.race():与 Promise.all()
类似,但是只要有一个 Promise 成功或失败,它就会立即执行。
Promise.all = Promise.all || function (promises) {
// 判断传入参数是否是数组
if(!Array.isArray*(promises)) {
throw new Error('Promise.all must accept an array'); // 如传入参数不是数组,抛出错误提示该函数必须传入一个数组作为参数
}
return new Promise((resolve,reject) => {
// 结果数组
const res = []
// 遍历参数
promises.forEach((item ,index)=> {
// 判断每个item是否是promise对象
if(!item instanceof Promise) {
reject(new Error('Promise.all must accept an array of promises')) // 如果不是promise对象,则抛出错误提示需要接受promise对象类型的数据
}
// 执行该对象
item.then(data => {
res[index] = data // 将执行结果存入结果数组中
index++ // 序号增加
// 若此时序号与传入参数长度一致,则说明已经遍历执行完所有的promise对象
if(index === promises.length) {
resolve(res) // 返回结果数组
}
},
reason => {
// 此时说明有promise对象执行失败,直接返回失败结果
reject(reason)
})
})
})
}
第二版中限制使用ES6相关的新特性,使用低版本实现。(面试官但还是限制使用const ,let,forEach等高级API)
Promise.all = Promise.all || function (promises) {
// 判断传入参数是否是数组
if(!Array.isArray(promises)) {
throw new Error('Promise.all must accept an array'); // 如传入参数不是数组,抛出错误提示该函数必须传入一个数组作为参数
}
return new Promise((resolve,reject) => {
// 结果数组
var res = []
for(var i = 0; i < promises.length; i++) {
// 由于promise是异步执行的,所有必须将每次的i通过函数参数的形式传递给then方法否则promise还未执行,i已经增加了
function handle(index) {
// 判断每个元素是否是promise对象
if(!promises[index] instanceof Promise) {
reject(new Error('Promise.all must accept an array of promises')) // 如果不是promise对象,则抛出错误提示需要接受promise对象类型的数据
}
promises[index].then(data => {
res[index] = data // 将执行结果存入结果数组中
index++ // 序号增加
// 若此时序号与传入参数长度一致,则说明已经遍历执行完所有的promise对象
if(index === promises.length) {
resolve(res) // 返回结果数组
}
},
reason => {
// 此时说明有promise对象执行失败,直接返回失败结果
reject(reason)
})
}
handle(i)
}
})
}
handle
函数和 let
声明避免了闭包问题,确保每个 Promise
都能正确地更新自己的索引位置。Promise
对象时立即拒绝,而方法一则可能在循环结束后才抛出错误。new Array(promises.length)
来初始化结果数组,这有助于避免索引问题。尽管两种方法都尝试实现 Promise.all
的功能,但方法二在处理异步操作和索引管理方面更为健壮和准确。然而,需要注意的是,这些自定义实现通常不应该用于生产环境,因为原生的 Promise.all
方法已经非常优化,并且经过了广泛的测试。自定义实现可能会引入难以预料的bug,特别是在处理复杂的异步逻辑时。