前言
在面试之中关于promise
经常被问起!这也许是因为我们的js充满了异步函数,而promise
是用来处理异步操作的重要方案,所以今天让我们来全面的学习一下🚀🚀🚀
介绍
Promise
是 JavaScript 异步编程的重要知识点,既是面试常客,也是高频考点。接下来让我们从 核心概念 、API 详解 、手写实现 、经典面试题 、实践例题 五个角度拿下promise
。
1.核心概念
1.promise是什么?
简单来说就是一种异步编程的模式,用于处理异步操作且可以避免出现回调地狱的问题,它表示一个异步操作,这个操作可能完成也可能失败,并将结果,错误通过状态机制传递。
2.promise的三种状态
pending
(待定) :初始状态,异步操作尚未完成。fulfilled
(已完成) :异步操作成功,调用.then()
。rejected
(已拒绝) :异步操作失败,调用.catch()
。
⚠️
Promise
状态一旦从pending
转变为fulfilled
或rejected
,就不可逆。
✅
你可以将 Promise 视为一个容器,用于封装未来的值(异步操作的结果)。通过 .then() 方法可以处理 fulfilled 的结果,通过 .catch() 方法可以捕获 rejected 的错误。
Promise API详解
✅ 1. Promise.prototype.then()
- 接收两个回调函数(
onFulfilled
,onRejected
),分别处理成功和失败,onFulfilled
的值就是Promise 被 resolved 时返回的对象,同样的onRejected
。 .then()
返回一个新的Promise
,因此可链式调用。
⚠️
.then()
中若没有返回值,默认返回undefined
。
js
function fetchData() {
return new Promise((resolve, reject) => {
// 模拟网络请求,1 秒后返回结果
setTimeout(() => {
const success = true; // 假设请求成功
if (success) {
resolve("Data fetched successfully");
} else {
reject("Failed to fetch data");
}
}, 1000);
});
}
fetchData()
.then(
(result) => {
console.log("Success:", result); // 处理成功的回调
return "Processed data"; // 返回一个新的promise对象
},
(error) => {
console.error("Error:", error); // 处理失败的回调
}
)
.then((processedResult) => {
console.log("Processed Result:", processedResult); // 处理上一个 then 返回的结果
})
.catch((err) => {
console.error("Caught Error:", err); // 捕获整个链中的错误
});
在上面的例子中我们给.then传了两个回调函数,一个处理成功 一个处理失败,但一般情况下我们更喜欢使用.catch()来处理promise的失败情况。
⚠️ 还有一个值得注意的是
.then()
中无论你返回什么,都会被包装成一个Promise
对象,就比如你返回一个普通值也会被包装成Promise.resolve(),这保证了promise
的链式调用。
✅ 2. Promise.prototype.catch()
- 专门捕获
Promise
中的异常或.then()
的失败回调。
这没什么好讲的,我们可以在链式中使用.catch()
来捕获失败或异常同样的.catch()
的返回始终是一个promise对象,默认为underfined。
✅ 3. Promise.prototype.finally()
- 无论
Promise
成功或失败,finally()
都会执行。 - 通常用于资源释放 、加载动画关闭等场景。
✅ 4. Promise.all()
- 接收一个
Promise
数组,全部成功 时才会进入.then()
;任意一个失败,立即进入.catch()
。 - 适用于所有任务必须成功的场景。
js
Promise.all([
Promise.resolve("任务1完成"),
Promise.resolve("任务2完成"),
Promise.reject("任务3失败")
])
.then(console.log) // 不会执行
.catch(console.error); // 输出:"任务3失败"
promise.all()
在实际的应用场景很多,比如在处理并发异步任务的时候,我们在项目中经常需要同时请求多个接口的数据,例如在查看你的掘金个人主页的时候需要同时获取你的信息,你的文章数据等。
javascript
function getUserInfo() {
return fetch("https://jsonplaceholder.typicode.com/users/1").then(res => res.json());
}
function getPosts() {
return fetch("https://jsonplaceholder.typicode.com/posts").then(res => res.json());
}
function getComments() {
return fetch("https://jsonplaceholder.typicode.com/comments").then(res => res.json());
}
Promise.all([getUserInfo(), getPosts(), getComments()])
.then(([user, posts, comments]) => {
console.log("用户信息:", user);
console.log("文章数据:", posts);
console.log("评论数据:", comments);
})
.catch(error => {
console.error("某个请求失败:", error);
});
其还常用于加载多个静态资源
js
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = src;
img.onload = () => resolve(`${src} 加载成功`);
img.onerror = () => reject(`${src} 加载失败`);
});
}
Promise.all([
loadImage('image1.jpg'),
loadImage('image2.jpg'),
loadImage('image3.jpg')
])
.then(results => console.log("全部图片加载成功:", results))
.catch(error => console.error("某张图片加载失败:", error));
例如并行加载多张图片,其可以提高页面的性能,promise.all()
确保所有的图片加载完成了以后才会.then()操作非常适合轮播图,和懒加载等场景!
✅ 5. Promise.race()
- 接收一个
Promise
数组,第一个完成 的Promise
(无论成功或失败)就决定最终结果。 - 常用于请求超时控制。
js
Promise.race([
new Promise(resolve => setTimeout(() => resolve("请求成功"), 1000)), // 请求成功的时间改为1秒
new Promise((_, reject) => setTimeout(() => reject("请求超时"), 2000)) // 超时时间保持为2秒
])
.then(console.log) // 输出:"请求成功"
.catch(console.error);
这里我们设置的在一秒时间内完成了请求就会输出请求成功,但要是由于网络延迟、服务器响应慢等原因未能在1秒内完成,且两秒后还没完成就会输出请求超时。
✅ 6. Promise.allSettled()
-
接收一个
Promise
数组,无论成功或失败都会将所有结果返回。 -
每个结果对象包含:
{ status: "fulfilled", value: 数据 }
{ status: "rejected", reason: 错误信息 }
就例如我们在批量删除时,即使有几项删除失败,其他仍可以删除成功。相同的其还经常应用在备份数据,用户并行操作等。
Promise 经典面试题
1.与event loop结合考察执行顺序
Promise
的 .then()
属于微任务 ,在当前同步代码执行完毕后执行。 具体的关于event loop的介绍可以看我这篇文章:envent Loop
2. Promise 链式调用中的返回值
js
Promise.resolve(1)
.then(x => x + 1)
.then(x => { throw new Error("出错了"); })
.catch(err => console.error(err.message))
.then(x => console.log("继续执行", x));
javascript
答案:
出错了
继续执行 undefined
执行过程分析:
首先1会传到第一个.then()中然后返回一个2进入第二个.then()中,其抛出了一个错误,被catch捕获并输出:出错了,因其没有设置返回值默认返回underfined,underfined传入下一个then输出:继续执行 underfined
3. 手写 Promise 实现(高频)
首先我们先整理一下思路:
-
状态(State) :一个
Promise
对象有三种可能的状态:pending
(等待中)fulfilled
(已完成/成功)rejected
(已拒绝/失败)
-
函数参数 :
resolve
和reject
resolve
:异步操作成功后调用,将promise的状态变为fulfilled
并将异步操作的结果传递给.then()
方法中的成功回调。reject
:异步操作失败后调用,将promise的状态变为rejected
将异步操作失败的原因传递给.catch()
方法。 3.链式调用 :.then()
要返回一个新的promise对象以支持链式调用。
kotlin
手写代码
class MyPromise {
constructor(executor) {
this.status = "pending"; // 初始状态
this.value = undefined; // 成功的值
this.reason = undefined; // 失败的原因
// 用于存储回调函数,确保异步情况也能调用
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === "pending") {
this.status = "fulfilled";
this.value = value;
// 执行所有成功回调
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.status === "pending") {
this.status = "rejected";
this.reason = reason;
// 执行所有失败回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject); // 立即执行传入的执行器函数
} catch (error) {
reject(error); // 捕获同步错误
}
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
if (this.status === "fulfilled") {
try {
const result = onFulfilled(this.value);
resolve(result); // 将结果传递给下一个 then
} catch (error) {
reject(error);
}
}
if (this.status === "rejected") {
try {
const result = onRejected(this.reason);
resolve(result); // 继续执行下一个 then(如果有返回值)
} catch (error) {
reject(error);
}
}
if (this.status === "pending") {
// 异步情况,存储回调
this.onFulfilledCallbacks.push(() => {
try {
const result = onFulfilled(this.value);
resolve(result);
} catch (error) {
reject(error);
}
});
this.onRejectedCallbacks.push(() => {
try {
const result = onRejected(this.reason);
resolve(result);
} catch (error) {
reject(error);
}
});
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
finally(callback) {
return this.then(
value => {
callback();
return value;
},
reason => {
callback();
throw reason;
}
);
}
}
经典面试题
-
Promise
的状态一旦变更,是否可以再改变? -
如何使用
Promise.race()
实现请求超时控制? -
Promise 和
async/await
有什么异同?
🎯 总结
✅ Promise
的三种状态及其不可逆性
✅ Promise
链式调用原理
✅ .then()
、.catch()
、.finally()
的执行顺序
✅ Promise.all()
vs Promise.race()
vs Promise.any()
✅ Promise
与 async/await
的区别
✅ Promise
与 setTimeout
(微任务 vs 宏任务)
✅ 手写 Promise
实现