Promise是ES6提出的对于异步编程的解决方案,对回调函数进行包装,可以解决异步编程回调地狱的问题,降低了编码难度且代码可读性更高。
Promise是什么
Promise意为承诺,承诺一个异步操作会在将来兑现。本质上是一个对象,这个对象有三种状态,分别是pending(进行中),fulfilled(成功)和rejected(失败)。 Promise实例被声明时会立即执行,中途无法取消,状态确定下来后也无法再更改。
Promise的构造函数接收一个函数,该函数有两个参数(resolve和reject),分别是成功和失败要执行的操作,如果执行resolve,状态就会从pending变成fulfilled;如果执行reject,状态就会从pending变成rejected。
Promise实例有三个方法,分别是then()、catch()、finally(),我们可以在其中定义成功状态、失败状态和最终要触发的回调函数,这些回调函数的参数就是前面用resolve或rejected抛出的参数。而且then和catch方法会返回一个新的promise对象,这个对象也有自己的状态,可以定义相应的回调函数,而这些回调函数又会返回promise对象......就可以这样无限套娃下去,直到遇到finally执行最终的操作。写起来是链式的,执行起来是嵌套的。
ps:其实then可以接收两个回调,第二个是reject回调,但我们一般不会这么写,而是把reject的回调放在catch里
js
// deepseek写的演示代码
const promise = new Promise((resolve) => {
resolve("操作成功"); // 直接触发成功状态
})
.catch((error) => {
console.log("这行会被跳过[0] catch:", error);
})
.then((result) => {
console.log("[1] then:", result); // 接收成功结果
return "加工后的数据"; // 传递新值给下一个 then
})
.then((processedData) => {
console.log("[2] then:", processedData); // 处理加工后的数据
throw new Error("模拟中途错误"); // 主动抛出错误
})
.catch((error) => {
console.log("[3] catch:", error.message); // 捕获错误
return "错误恢复后的数据"; // 返回新值继续链式调用
})
.finally(() => {
console.log("[4] finally: 清理资源"); // 无论成功失败都会执行
})
// [1] then: 操作成功
// [2] then: 加工后的数据
// [3] catch: 模拟中途错误
// [4] finally: 清理资源
Promise的静态方法
回顾promise常用的静态方法并手写一下
promise.all
用于将一个promise实例数组(可迭代对象)包装成一个新的promise实例 。
只有数组中所有promise状态都是fulfilled ,新promise状态才是fulfilled ,且将所有promise的返回值组成数组,传给新promise的回调函数;否则只要有一个rejected ,新promise的状态就变成rejected,且第一个rejected的promise的返回值传给新promise的回调,特别的,如果rejected的promise有自己的catch,就执行自己的catch,不触发新promise的catch。
js
const p1 = new Promise((res, rej) => res('p1成功'))
const p2 = new Promise((res, rej) => res('p2成功'))
const p3 = new Promise((res, rej) => rej('p3失败'))
const p4 = new Promise((res, rej) => rej('p4失败'))
Promise.all([p1, p2]).then(res => {
console.log(res) // [ 'p1成功', 'p2成功' ]
}).catch(err => {
console.log(err)
})
Promise.all([p1, p2, p3, p4]).then(res => {
console.log(res)
}).catch(err => {
console.log(err) // p3失败
})
promise.all的实现思路是怎样的呢?我们来模拟一下
- 返回值是一个新的promise
- 先把可迭代对象转换为数组,并开一个新数组用于返回fulfilled结果
- 遍历数组中的每个promise,依次把它们resolve转换成promise值,如果正常返回,就把返回值存到结果数组中,如果碰到一个错误,就立即抛出并rejected
- 在遍历数组时实时更新未处理的promise的数量,如果已经处理完所有promise,才resolve
js
Promise.myAll = function(promises) {
return new Promise((resolve, reject) => {
try {
// 1. 将可迭代对象转换为数组(若不可迭代,此处会抛错)
const iterable = Array.from(promises);
// 2. 空数组直接成功
if (iterable.length === 0) {
resolve([]);
return;
}
const results = new Array(iterable.length); // 按顺序保存结果
let remaining = iterable.length; // 未完成的 Promise 数量
// 3. 遍历每个 Promise
iterable.forEach((item, index) => {
// 4. 将元素转换为 Promise(兼容非 Promise 值)
Promise.resolve(item)
.then((value) => {
results[index] = value; // 按索引保存结果
remaining--;
// 5. 全部成功时解析结果
if (remaining === 0) resolve(results);
})
.catch(reject); // 6. 任意失败立即拒绝
});
} catch (error) {
// 参数不可迭代时拒绝
reject(error);
}
});
}
通过all可以将多个请求合并在一起,汇总所有请求的结果,只需要一个loading即可
promise.race
和promise.all类似,不过新promise的状态取决于第一个状态发生改变的promise,并把这个promise的返回值传给新promise的回调。如果超过规定时间仍没有promise状态发生改变,则新promise的状态为rejected。根据这个特性,race可以用于设置图片请求超时
功能演示如下:
js
// 第一个完成的 Promise 是成功的
Promise.race([
new Promise(resolve => setTimeout(() => resolve(1), 2000)),
new Promise(resolve => setTimeout(() => resolve(2), 1000)),
]).then(console.log); // 输出(1秒后): 2
// 第一个完成的 Promise 是失败的
Promise.race([
new Promise((_, reject) => setTimeout(() => reject("Error"), 1000)),
new Promise(resolve => setTimeout(() => resolve(2), 2000)),
]).catch(console.error); // 输出(1秒后): Error
race手写起来就比all简单很多了,只需要返回第一个做出响应的promise即可
js
Promise.myRace = function(promises) {
return new Promise((resolve, reject) => {
try {
// 1. 将可迭代对象转换为数组(若不可迭代,此处会抛错)
const iterable = Array.from(promises);
// 2. 遍历每个 Promise
iterable.forEach((item) => {
// 3. 将元素转换为 Promise(兼容非 Promise 值)
Promise.resolve(item) // 4. 哪个先完成就返回哪个
.then(resolve)
.catch(reject);
});
} catch (error) {
// 参数不可迭代时拒绝
reject(error);
}
});
}
promise.allSettled
和上面的方法参数一样,不过是要等到所有promise都返回结果,不管是成功还是失败,新promise的状态永远是成功。把所有promise的结果存在数组里传给新promise的回调。就不手写了
promise.resolve和promise.reject
将现有对象转换成promise实例,状态为resolved/rejected
手写promise
js
class MyPromise {
constructor(executor) {
this.state = 'pending'; // 初始状态
this.value = undefined; // 成功值
this.reason = undefined; // 失败原因
this.onFulfilledCallbacks = []; // 成功回调队列
this.onRejectedCallbacks = []; // 失败回调队列
// 定义 resolve 函数
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 异步触发回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
// 定义 reject 函数
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
// 异步触发回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 执行器立即执行,捕获错误
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
// 实现 then 方法
then(onFulfilled, onRejected) {
// 处理值穿透(例如 then() 不传回调)
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
// 返回新 Promise 以实现链式调用
const promise2 = new MyPromise((resolve, reject) => {
// 统一处理回调
const handleCallback = (callback, value) => {
setTimeout(() => { // 模拟异步
try {
const result = callback(value);
// 处理返回 Promise 的情况
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
}, 0);
};
// 当前 Promise 已完成
if (this.state === 'fulfilled') {
handleCallback(onFulfilled, this.value);
}
// 当前 Promise 已失败
else if (this.state === 'rejected') {
handleCallback(onRejected, this.reason);
}
// 当前 Promise 未完成,存储回调
else {
this.onFulfilledCallbacks.push(() => handleCallback(onFulfilled, this.value));
this.onRejectedCallbacks.push(() => handleCallback(onRejected, this.reason));
}
});
return promise2;
}
// 实现 catch 方法
catch(onRejected) {
return this.then(null, onRejected);
}
// 静态 resolve 方法
static resolve(value) {
return new MyPromise(resolve => resolve(value));
}
// 静态 reject 方法
static reject(reason) {
return new MyPromise((_, reject) => reject(reason));
}
}
js
// 测试链式调用
const p = new MyPromise((resolve) => {
setTimeout(() => resolve('成功'), 1000); // 模拟异步
});
p.then((value) => {
console.log('第一次 then:', value); // 1秒后输出: 第一次 then: 成功
return MyPromise.resolve('新 Promise');
})
.then((value) => {
console.log('第二次 then:', value); // 输出: 第二次 then: 新 Promise
throw new Error('测试错误');
})
.catch((error) => {
console.log('捕获错误:', error.message); // 输出: 捕获错误: 测试错误
});