then、catch、finally
- Promise 的状态一经改变就不能再改变
- .then 和.catch 都会返回一个新的 Promise
- catch 不管被连接到哪里,都能捕获上层的错误
- 在 Promise 中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如 return 2 会被包装为 return Promise.resolve(2)
- Promise 的 .then 或者 .catch 可以被调用多次, 当如果 Promise 内部的状态一经改变,并且有了一个值,那么后续每次调用.then 或者.catch 的时候都会直接拿到该值
- .then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获
- .then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
- .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透
- .then 方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候可以认为 catch 是.then 第二个参数的简便写法
- .finally 方法也是返回一个 Promise,他在 Promise 结束的时候,无论结果为 resolved 还是 rejected,都会执行里面的回调函数
题目一
const promise = new Promise((resolve, reject) => {
resolve("success1");
reject("error");
resolve("success2");
});
promise
.then((res) => {
console.log("then: ", res);
})
.catch((err) => {
console.log("catch: ", err);
});
// "then: success1"
构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用 。验证了第一个结论,Promise 的状态一经改变就不能再改变
题目二
const promise = new Promise((resolve, reject) => {
reject("error");
resolve("success2");
});
promise
.then((res) => {
console.log("then: ", res);
})
.then((res) => {
console.log("then: ", res);
})
.catch((err) => {
console.log("catch: ", err);
})
.then((res) => {
console.log("then: ", res);
});
// "catch: " "error"
// "then3: " undefined
验证了第三个结论,catch 不管被连接到哪里,都能捕获上层的错误。
题目三
Promise.resolve(1)
.then((res) => {
console.log(res);
return 2;
})
.catch((err) => {
return 3;
})
.then((res) => {
console.log(res);
});
// 1
// 2
Promise 可以链式调用,不过 promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用, 它并不像一般任务的链式调用一样 return this
上面的输出结果之所以依次打印出 1 和 2,那是因为 resolve(1)之后走的是第一个 then 方法,并没有走 catch 里,所以第二个 then 中的 res 得到的实际上是第一个 then 的返回值
且 return 2 会被包装成 resolve(2)
题目四
如果把 3 中的 Promise.resolve(1)改为 Promise.reject(1)又会怎么样呢?
Promise.reject(1)
.then((res) => {
console.log(res);
return 2;
})
.catch((err) => {
console.log(err);
return 3;
})
.then((res) => {
console.log(res);
});
// 1
// 3
结果打印的当然是 1 和 3,因为 reject(1)此时走的就是 catch,且第二个 then 中的 res 得到的就是 catch 中的返回值
题目五
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("timer");
resolve("success");
}, 1000);
});
const start = Date.now();
promise.then((res) => {
console.log(res, Date.now() - start);
});
promise.then((res) => {
console.log(res, Date.now() - start);
});
// 'timer'
// success 1001
// success 1002
当然,如果足够快的话,也可能两个都是 1001。 Promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值
题目六
Promise.resolve()
.then(() => {
return new Error("error!!!");
})
.then((res) => {
console.log("then: ", res);
})
.catch((err) => {
console.log("catch: ", err);
});
可能想到的是进入.catch 然后被捕获了错误。
结果并不是这样的,它走的是.then 里面:
"then: " "Error: error!!!"
这也验证了第 4 点和第 6 点,返回任意一个非 promise 的值都会被包裹成 promise 对象,因此这里的 return new Error('error!!!')也被包裹成了 return Promise.resolve(new Error('error!!!'))
当然如果抛出一个错误的话,可以用下面 👇 两的任意一种:
return Promise.reject(new Error("error!!!"));
// or
throw new Error("error!!!");
题目七
const promise = Promise.resolve().then(() => {
return promise;
});
promise.catch(console.err);
.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。
因此结果会报错:
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
题目八
Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log);
其实只要记住原则 8:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
第一个 then 和第二个 then 中传入的都不是函数,一个是数字类型,一个是对象类型,因此发生了穿透,将 resolve(1) 的值直接传到最后一个 then 里。
所以输出结果为:1
题目九
下面来介绍一下.then 函数中的两个参数。
第一个参数是用来处理 Promise 成功的函数,第二个则是处理失败的函数。 也就是说 Promise.resolve('1')的值会进入成功的函数,Promise.reject('2')的值会进入失败的函数。
来看看这个例子
Promise.reject("err!!!")
.then(
(res) => {
console.log("success", res);
},
(err) => {
console.log("error", err);
}
)
.catch((err) => {
console.log("catch", err);
});
这里的执行结果是:
'error' 'error!!!'
它进入的是 then()中的第二个参数里面,而如果把第二个参数去掉,就进入了 catch()中:
Promise.reject("err!!!")
.then((res) => {
console.log("success", res);
})
.catch((err) => {
console.log("catch", err);
});
执行结果:
'catch' 'error!!!'
如果是这个案例呢?
Promise.resolve()
.then(
function success(res) {
throw new Error("error!!!");
},
function fail1(err) {
console.log("fail1", err);
}
)
.catch(function fail2(err) {
console.log("fail2", err);
});
由于 Promise 调用的是 resolve(),因此.then()执行的应该是 success()函数,可是 success()函数抛出的是一个错误,它会被后面的 catch()给捕获到,而不是被 fail1 函数捕获
因此执行结果为:
fail2 Error: error!!!
at success
题目十
接着来看看.finally(),这个功能一般不太用在面试中,不过如果碰到了也应该知道该如何处理。
function promise1() {
let p = new Promise((resolve) => {
console.log("promise1");
resolve("1");
});
return p;
}
function promise2() {
return new Promise((resolve, reject) => {
reject("error");
});
}
promise1()
.then((res) => console.log(res))
.catch((err) => console.log(err))
.finally(() => console.log("finally1"));
promise2()
.then((res) => console.log(res))
.catch((err) => console.log(err))
.finally(() => console.log("finally2"));
结果:
'promise1'
'1'
'error'
'finally1'
'finally2'
综合题
题目一
const first = () =>
new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
console.log(p);
}, 0);
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
});
first().then((arg) => {
console.log(arg);
});
console.log(4);
第一段代码定义的是一个函数,所以得看看它是在哪执行的,发现它在 4 之前,所以可以来看看 first 函数里面的内容了。
函数 first 返回的是一个 new Promise(),因此先执行里面的同步代码 3
接着又遇到了一个 new Promise(),直接执行里面的同步代码 7
执行完 7 之后,在 p 中,遇到了一个定时器,先将它放到下一个宏任务队列里不管它,接着向下走
碰到了 resolve(1),这里就把 p 的状态改为了 resolved,且返回值为 1,不过这里也先不执行
跳出 p,碰到了 resolve(2),这里的 resolve(2),表示的是把 first 函数返回的那个 Promise 的状态改了,也先不管它。
然后碰到了 p.then,将它加入本次循环的微任务列表,等待执行
跳出 first 函数,遇到了 first().then(),将它加入本次循环的微任务列表(p.then 的后面执行)
然后执行同步代码 4
本轮的同步代码全部执行完毕,查找微任务列表,发现 p.then 和 first().then(),依次执行,打印出 1 和 2
本轮任务执行完毕了,发现还有一个定时器没有跑完,接着执行这个定时器里的内容,执行同步代码 5
然后又遇到了一个 resolve(6),它是放在 p 里的,但是 p 的状态在之前已经发生过改变了,因此这里就不会再改变,也就是说 resolve(6)相当于没任何用处,因此打印出来的 p 为 Promise{
3
7
4
1
2
5
Promise{
题目二
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve("resolve3");
console.log("timer1");
}, 0);
resolve("resovle1");
resolve("resolve2");
})
.then((res) => {
console.log(res);
setTimeout(() => {
console.log(p1);
}, 1000);
})
.finally((res) => {
console.log("finally", res);
});
注意的知识点:
Promise 的状态一旦改变就无法改变
finally 不管 Promise 的状态是 resolved 还是 rejected 都会执行,且它的回调函数是没有参数的
'resolve1'
'finally' undefined
'timer1'
Promise{
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南