一文搞懂 Promise(最全面)
1.Promise
1.Promise 所有常用方法
Promise 是 JavaScript 中用于处理异步操作的对象,它提供了一些常用的方法和状态来处理异步操作的结果。以下是 Promise 的常用方法以及它们的详细说明:
1.Promise 构造函数
Promise 构造函数用于创建一个新的 Promise 对象。它接受一个执行器函数,该函数有两个参数:resolve
和 reject
,分别用于成功和失败时的处理。
const promise = new Promise((resolve, reject) => {
// 异步操作
if (/* 异步操作成功 */) {
resolve(result); // 成功时调用 resolve
} else {
reject(error); // 失败时调用 reject
}
});
2.then 方法
Promise 实例上的 then
方法用于注册成功和失败时的回调函数。它接受两个回调函数作为参数,第一个参数是成功时的回调,第二个参数是失败时的回调。
promise.then(
(result) => {
// 处理成功结果
},
(error) => {
// 处理失败情况
}
);
then 方法的第二个参数等价于 catch 嘛?
then
方法的第二个参数和 catch
方法在**处理 Promise 的失败情况上有相似之处,**但它们之间存在一些重要的区别:
then 方法的第二个参数:
then
方法的第二个参数是一个回调函数,用于处理 Promise 的失败情况。- 如果在
then
方法的第一个参数回调中发生了错误(抛出异常),第二个参数回调不会捕获这个错误。 - 如果在
then
方法的第一个参数回调中返回了一个拒绝状态的 Promise,第二个参数回调也不会捕获这个拒绝状态,而是将其传递给下一个catch
方法。
jspromise.then( (result) => { // 处理成功情况 }, (error) => { // 处理失败情况 } );
catch 方法:
catch
方法是专门用于捕获 Promise 链中的错误的方法。它接受一个回调函数,用于处理 Promise 的失败情况。- 如果在 Promise 链中的任何位置发生错误,
catch
方法都会捕获这个错误。 catch
方法通常用于处理 Promise 链中的错误,以确保在出现错误时能够适当地处理它。
jspromise.catch((error) => { // 处理失败情况 });
总之,虽然 then
方法的第二个参数和 catch
方法都用于处理 Promise 的失败情况,但它们的使用场景和行为略有不同。通常,建议在 Promise 链中使用 catch
方法来一致地捕获和处理错误,以确保代码的可维护性和一致性。
3.catch 方法
catch
方法用于捕获 Promise 链中的错误,它接受一个失败回调函数作为参数。
promise.catch((error) => {
// 处理错误
});
4.finally 方法
finally
方法用于注册一个回调函数,在 Promise 执行完毕后无论成功还是失败都会被调用。它没有参数。
promise.finally(() => {
// 不论成功还是失败都会执行
});
5.Promise.resolve 方法
Promise.resolve
方法返回一个已解决(resolved)的 Promise 对象,可以用来快速创建一个成功的 Promise。
const resolvedPromise = Promise.resolve(result);
6.Promise.reject 方法
Promise.reject
方法返回一个已拒绝(rejected)的 Promise 对象,用来快速创建一个失败的 Promise。
const rejectedPromise = Promise.reject(error);
7.Promise.all 方法
Promise.all
方法接受一个可迭代对象(通常是数组),返回一个新的 Promise,该 Promise 在所有输入的 Promise 都解决后才会解决,返回的值是一个包含所有 Promise 结果的数组。
const promises = [promise1, promise2, promise3];
Promise.all(promises)
.then((results) => {
// 所有 Promise 都已解决,results 包含各个 Promise 的结果
})
.catch((error) => {
// 任何一个 Promise 失败都会导致整个 Promise.all 失败
});
Promise.all 方法在执行 promise 的时候如果有被拒绝的呢?
Promise.all
方法在以下情况下会被拒绝(rejected):
- 任何一个输入的 Promise 被拒绝:如果
Promise.all
的输入参数中的任何一个 Promise 被拒绝,那么Promise.all
返回的 Promise 也会被拒绝(也就是说所有结果都收不到了,只能走.catch 了)。 - 输入参数为空数组:如果
Promise.all
的输入参数是一个空数组,它会立即解决为一个空数组,而不会等待其他 Promise 的状态。
以下是示例代码来说明这些情况:
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve(3);
const promises = [promise1, promise2, promise3];
Promise.all(promises)
.then((results) => {
console.log(results); // 这里不会执行,因为 promise2 被拒绝
})
.catch((error) => {
console.error(error); // 输出: "Error",因为 promise2 被拒绝
});
在这个示例中,promise2
被拒绝,因此 Promise.all
返回的 Promise 也会被拒绝。then
方法中的回调不会执行,而是立即进入 catch
方法中处理拒绝的错误。
因此,使用 Promise.all
时,需要注意处理可能的拒绝情况,以确保代码在遇到错误时能够适当地处理。如果你希望在部分 Promise 被拒绝时也能够获取已解决的结果,可以考虑使用 Promise.allSettled
方法。此方法会等待所有 Promise 的状态(无论是解决还是拒绝)都被处理,并返回一个包含每个 Promise 结果的数组,其中每个结果都带有状态信息。这样可以更灵活地处理各种情况。
8.Promise.race 方法
Promise.race
方法也接受一个可迭代对象,返回一个新的 Promise,该 Promise 在输入的 Promise 中有一个解决或拒绝时就会解决或拒绝,返回的值是第一个解决或拒绝的 Promise 结果。
const promises = [promise1, promise2, promise3];
Promise.race(promises)
.then((result) => {
// 第一个 Promise 解决或拒绝时触发
})
.catch((error) => {
// 第一个 Promise 解决或拒绝时触发
});
这些是 Promise 的常用方法,它们帮助你更轻松地处理异步操作的结果、错误和状态。使用 Promise 可以有效地编写和组织异步代码,使其更加清晰和可维护。
9.Promise.allSettled 方法
Promise.allSettled
是 JavaScript 中的一个 Promise 静态方法,用于等待所有给定的 Promise 对象都已经"settled"(状态变为 resolved 或 rejected)后返回一个 Promise。
与 Promise.all
不同,Promise.allSettled
不会在任何一个 Promise 被拒绝时立即返回拒绝状态,而是等待所有 Promise 都完成,并返回一个包含每个 Promise 结果的数组,每个结果都带有状态信息(最终返回的一定是一个 resolved 状态的 promise,只能使用 then 处理)。
Promise.allSettled
的语法如下:
Promise.allSettled(iterable);
iterable
:一个可迭代对象,通常是一个包含多个 Promise 的数组或类数组对象。
Promise.allSettled
返回一个新的 Promise,该 Promise 在所有输入的 Promise 都被 settled 后解决,其解决值是一个包含每个 Promise 结果的数组。每个结果都是一个对象,包含以下属性:
status
:表示 Promise 的状态,可能的值是"fulfilled"
(已解决)或"rejected"
(已拒绝)。value
(仅在状态为"fulfilled"
时存在):表示 Promise 解决时的值。reason
(仅在状态为"rejected"
时存在):表示 Promise 被拒绝时的原因(错误信息)。
以下是一个示例:
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject("Error");
const promise3 = Promise.resolve(3);
const promises = [promise1, promise2, promise3];
Promise.allSettled(promises).then((results) => {
results.forEach((result, index) => {
if (result.status === "fulfilled") {
console.log(`Promise ${index + 1} resolved with value:`, result.value);
} else {
console.error(
`Promise ${index + 1} rejected with reason:`,
result.reason
);
}
});
});
在这个示例中,Promise.allSettled
会等待所有 Promise 完成,然后返回一个数组,包含每个 Promise 的状态和结果。你可以根据 status
属性来判断每个 Promise 的状态,然后分别处理已解决和已拒绝的情况。
Promise.allSettled
在需要等待多个异步操作完成,并获取它们的结果,而不希望其中任何一个操作的失败立即终止整个流程时非常有用。它允许你在最终处理每个 Promise 的结果,无论它们是成功还是失败时,都能够获得完整的信息。
2.async/await
1.async/await 是什么
async/await 是 ES7 提出的基于 Promise 的解决异步的最终方案。
2.async
async 是一个加在函数前的修饰符,被 async 定义的函数会默认返回一个 Promise 对象 resolve 的值。
因此对 async 函数可以直接 then,返回值就是 then 方法传入的函数。
// async基础语法
async function fun0(){
console.log(1);
return 1;
}
fun0().then(val=>{
console.log(val) // 输出:1,1
})
async function fun1(){
console.log('Promise');
return new Promise(function(resolve,reject){
resolve('Promise')
})
}
fun1().then(val => {
console.log(val); // 输出:Promise Promise
})
3.await
await 也是一个修饰符,只能放在 async 定义的函数内。可以理解为等待。
await 修饰的如果是 Promise 对象:可以获取 Promise 中返回的内容(resolve 或 reject 的参数),且取到值后语句才会往下执行;——> 所以说加了 await 就不需要写.then()了
如果不是 Promise 对象:把这个非 promise 的东西当做 await 表达式的结果。
async function fun(){
let a = await 1;
let b = await new Promise((resolve,reject)=>{
setTimeout(function(){
resolve('setTimeout')
},3000)
})
let c = await function(){
return 'function'
}()
console.log(a,b,c)
}
fun(); // 3秒后输出: 1 "setTimeout" "function"
function log(time){
setTimeout(function(){
console.log(time);
return 1;
},time)
}
async function fun(){
let a = await log(1000);
let b = await log(3000);
let c = log(2000);
console.log(a);
console.log(1)
}
fun();
// 立即输出 undefined 1
// 1秒后输出 1000
// 2秒后输出 2000
// 3秒后输出 3000
4.async/await 的正确用法
// 使用async/await获取成功的结果
// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('获取成功')
},3000)
})
}
async function test(){
let a = await getSomeThing();
//这里就是.then的逻辑
console.log(a)
}
test(); // 3秒后输出:获取成功
5.对 async/await 的深入理解
使用 async / await,搭配 promise,==可以通过编写形似同步的代码来处理异步流程==,提高代码的简洁性和可读性。
5.1 await
引用 MDN 的介绍:
await
操作符用于等待一个Promise
对象, 它只能在异步函数async function
内部使用
await 的使用语法非常简单:
[return_value] = await expression;
其中 expression 是一个 Promise 对象或者任何要等待的值;
而 await expression 的执行结果有以下几种情况:
若 expression 是一个 Promise 对象, 并且其以值 x 被 fulfilled, 则返回值为 x
若 expression 是一个 Promise 对象, 并且其以异常 e 被 rejected, 则抛出异常 e
若 expression 不是 Promise 对象, 则会将 expression 处理成一个以 expression 值被 fulfilled 的 Promise 对象, 然后返回这个 Promise 对象的最终值 (即 expression 值)
这种用法没太大意义, 因此实际使用时还是尽量在 await 后跟一个 Promise 对象
另外需要注意的是, await 在等待 Promise 对象时会导致 async function 暂停执行, 一直到 Promise 对象决议之后才会 async function 继续执行!
通过一段代码来看一下:
async function foo() {
var a = await new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 2000);
});
console.log(a); // 第2秒时输出: 1
try {
var b = await new Promise((resolve, reject) => {
setTimeout(() => {
reject(2);
}, 1000);
});
} catch (e) {
console.log(e); // 第3秒时输出: 2
}
// 函数暂停2秒后再继续执行
var sleep = await new Promise((resolve) => {
setTimeout(() => {
resolve("sleep");
}, 2000);
});
var c = await 3;
console.log(c); // 第5秒时输出: 3
}
foo();
5.2 async
使用 async function 可以定义一个 异步函数, 语法为:
async function name([param[, param[, ... param]]]) {statements }
async 函数的返回值很特殊: 不管在函数体内 return 了什么值,async 函数的实际返回值总是一个 Promise 对象
详细讲就是:
若在
async
函数中return
了一个值x
, 不管x
值是什么类型,async
函数的实际返回值总是Promise.resolve(x)
.
那么 Promise.resolve(x) 最终返回一个什么样的 promise 呢?
来看一下 MDN 的介绍:
Promise.resolve(value)方法返回一个以给定值解析后的 Promise 对象。
但如果这个值是个 thenable(即带有 then 方法),返回的 promise 会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled);否则以该值为成功状态返回 promise 对象。
接下来看一段代码的应用. 假设有这样一个场景: 需要先通过 请求 1 拿到 数据 1, 然后通过通过 请求 2 并携带发送 数据 1 获取到 数据 2, 拿到 数据 2 之后再展示到页面
方案一:
async function showData() {
// 假装请求数据1
var data1 = await new Promise((resolve) => {
setTimeout(() => {
resolve("data1");
}, 1000);
});
// 假装请求数据2且此请求依赖数据1
var data2 = await new Promise((resolve) => {
setTimeout(() => {
resolve("data2");
}, 1000);
});
// 展示数据2
console.log(data2);
}
showData();
上面这段代码是依次获取到 数据 1 和 数据 2, 然后再展示 数据 2
既然 async 函数总是返回一个 promise, 那么也可以在一个 async 函数返回获取 数据 2 的 promise, 调用函数后再使用 then 方法拿到数据, 代码如下:
async function getData() {
// 假装请求数据1
var data1 = await new Promise((resolve) => {
setTimeout(() => {
resolve("data1");
}, 1000);
});
// 假装请求数据2且此请求依赖数据1
return new Promise((resolve) => {
setTimeout(() => {
resolve("data2");
}, 1000);
});
}
getData().then((v) => {
console.log(v);
});
6.自己的理解
1.async 的作用
就是为了配合 await 使用,保证这个 await 集合整体是一个异步的代码区域
2.await 的作用
不需要再写 then()方法,逻辑直接顺序地写在后面即可,让结构变得串行(new Promise 的返回值就是 then()方法的回调函数的参数)
3.新旧对比
//1.旧的写法:
const runAsync1 = () => {
let p = new Promise(function (resolve, reject) {
//做一些异步操作
setTimeout(function () {
console.log("执行完成Promise1");
resolve("要返回的数据可以任何数据例如接口返回数据");
}, 2000);
});
return p;
};
const runAsync2 = () => {
let p = new Promise(function (resolve, reject) {
//做一些异步操作
setTimeout(function () {
console.log("执行完成Promise2");
resolve("要返回的数据可以任何数据例如接口返回数据2");
}, 2000);
});
return p;
};
runAsync1() //一个Promise对象,可以then
.then(function (data) {
console.log(data);
return runAsync2(); //还是一个Promise对象,继续then
})
.then(function (data) {
console.log(data);
});
//2.新的写法:
async function test() {
var data1 = await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行完成Promise1");
resolve("要返回的数据可以任何数据例如接口返回数据1");
}, 2000);
});
//then方法的逻辑写在这里:
console.log(data1);
var data2 = await new Promise((resolve, reject) => {
setTimeout(() => {
console.log("执行完成Promise2");
resolve("要返回的数据可以任何数据例如接口返回数据2");
}, 2000);
});
//then方法的逻辑写在这里:
console.log(data2);
}
test();
结论:我们发现新的写法简化了很多代码,并且结构更加清晰,实现了通过编写形似同步的代码来处理异步流程!
4.实战写法
实战一般都是用在 ajax 请求,还有小程序的网络数据请求中
test.vue:
methods: {
// 获取轮播图数据的方法
async getSwiperList() {
// 1.发起请求
// 解构出Promise对象里面的data属性,重命名为res(这个res才是我们平常意义上的那个res,即后端返回的json对象)
const { data: res } = await uni.$http.get('/api/public/v1/home/swiperdata')
// 2.请求失败
if (res.meta.status !== 200) {
return uni.showToast({ //展示提示框
title: '数据请求失败!',
duration: 1500,
icon: 'none',
})
}
// 3.请求成功,为 data 中的数据赋值
this.swiperList = res.message
}
}
扩展:async 和 await 进一步理解
async
该关键字是放在函数之前的,使得函数成为一个异步函数,他最大的特点就是将函数封装成 Promise,也就是被他修饰的函数的返回值都是 Promise 对象。而这个 Promise 对象的状态则是由函数执行的返回值决定的。
如果返回的是一个非 promise 对象(同样会被包裹为 promise),该函数将返回一个成功的 Promise,成功的值则是返回的值;
如果返回的是一个 promise 对象,则该函数返回的就是该 promise 对应的状态。
——> 这个特点其实和 await 没什么关系,和.then的返回值包装规则比较类似。
await
await 右边是一个表达式,如果该表达式(异步函数)返回的是一个 Promise 对象,则左边接收的结果就是该 Promise 对象成功的结果,如果该 Promise 对象失败了,就必须使用 try..catch 来捕获。如果该表达式返回的不是一个 promise 对象,则左边接收的就是该表达式的返回值。
当 await 关键字与异步函数一起使用时,它的真正优势就变得明显了 —— 事实上, await 只在异步函数里面(async 关键字包裹的函数里面)才起作用。它可以放在任何异步的、基于 promise 的函数之前。它会暂停异步函数的代码在该行上,直到 promise 完成,然后返回结果值(将异步执行变为了同步)。
在暂停的同时,在外层异步函数所处在的外层同步代码中其他正在等待执行的代码就有机会执行了。
也就是下面这个说法:
当使用
await
表达式时,异步函数会在遇到异步操作时暂停执行,并允许其他代码继续执行。——> 这句话的解释?
其实也就是我们常说的,async+await 的组合让异步代码看起来像同步代码!但是实际上本来就是要用同步套异步,这里我们变成了异步套异步,但是外层的异步让我们用 await 变成同步了,所以本质上异步同步没有区别,只是写法会更加优雅易懂!
作用:这样可以实现非阻塞的异步操作,让程序能够同时处理多个任务,提高效率和响应性。
以下是一个示例,展示了使用 await
实现非阻塞异步操作的情况:
async function fetchData() {
console.log("Start fetching data...");
const response = await fetch("https://api.example.com/data"); // 等待异步网络请求完成
const data = await response.json(); // 等待将响应转换为 JSON
console.log("Data fetched:", data);
}
console.log("Before calling fetchData");
fetchData(); //因为fetchData这个异步函数里面有 await 关键字,才让这个异步函数可以不被等待就向下执行
console.log("After calling fetchData");
在上面的示例中,我们定义了一个异步函数 fetchData
,它使用 await
表达式来等待异步网络请求的完成和响应的解析。
在调用 fetchData
函数之前和之后,我们分别输出了两行日志。这样可以观察到异步函数的执行是非阻塞的,它在遇到 await
表达式时会暂停执行,让其他代码继续执行。
当我们运行上述代码时,输出的日志顺序如下:
Before calling fetchData
Start fetching data...
After calling fetchData //继续执行了同步代码
Data fetched: [data] //异步网络请求完成
可以看到,调用 fetchData
函数时,它会立即执行到第一个 await
表达式处,并暂停执行。然后,控制权返回给调用者,继续执行后续的代码,即输出 "After calling fetchData"。
在后台,异步网络请求在执行,直到完成。一旦网络请求完成,异步函数会恢复执行,继续执行后续的代码,并输出 "Data fetched: [data]"。
关于 await 的总结:
如果在异步函数中没有使用 await
表达式,调用者会等待异步函数执行完毕,但它不需要等待具体的异步操作完成,而是等待整个异步函数执行完毕后获取最终的返回值。
作用:使用了await
表达式,那么调用者就不需要等待异步函数执行完毕(让外层的异步变为不会阻塞的函数),而异步函数内部需要等待具体的异步操作完成(把内部的异步变为了同步)。
其实只要使用了 await,就至少有两层异步函数,被 async 包裹的函数本身就是一个大的异步函数(async 对于 await 来说的主要价值就是会把函数变为异步的),而 await 也是加在一个返回 promise 对象的异步函数上的(promise 本身就是用于将异步操作包装成一个对象,异步操作可以更加可控和易于处理),而 await 的主要作用就是把异步变为同步(以同步的方式编写异步代码,不用再.then 了),但是要发挥 await 的这个作用的代价就是要把 await 放在一个 async 异步函数里面,也就是 async 异步套 await 异步。
异步变同步的主要作用是简化 promise 状态流转(.then)的编写复杂度和样式;让异步代码看起来是同步的(实际上也变成同步的了),更易于逻辑的构建与编写(有些操作需要特定的先天条件,如 echarts 渲染需要数据)
总之,通过使用 "async" 和 "await",我们可以以同步的方式编写异步代码(直接把异步函数的 promise 当做同步的来操作),使其更易于阅读和理解。
举个 async+await 的应用例子:
f1 = () => {
return new Promise((resolve, reject) => {
// resolve(1);
reject("错误");
});
};
async function test() {
try {
const p = await f1();
console.log(p);
} catch (error) {
console.error(error);
}
}
test();
promise 的注意事项:
Promise 是 JavaScript 提供的一种用于处理异步操作的机制。它可以用于将异步操作包装成一个对象,使得异步操作可以更加可控和易于处理。
Promise 对象表示一个异步操作的最终完成或失败,并可以返回异步操作的结果或错误信息
但是new Promise((resolve, reject) => { ... })
中的代码不一定是异步函数,但通常用于处理异步操作。也就是说 Promise 也不是一定用来处理异步操作的!
在 new Promise
的构造函数中,我们可以执行任意代码逻辑,包括同步操作和异步操作。一般情况下,我们在 Promise 构造函数中执行的是异步操作,例如进行网络请求、读取文件、定时器等等。
使用 Promise 的主要目的是为了处理异步操作的结果,并将其封装成一个可处理的对象,以便后续进行链式操作或使用 .then
和 .catch
方法处理结果和错误。
3.Promise.then 的包装规则以及链式调用法则
在 Promise 的链式调用中,.then()
方法的返回值会影响后续的 Promise 状态和值。下面是一些关于 .then()
返回值的规则:
如果
.then()
方法中的回调函数返回一个值(非 Promise 对象),则返回的 Promise 对象将会处于已解决(fulfilled)状态,并且该值将作为解决值传递给下一个.then()
方法的回调函数。例如:jssomeAsyncFunction() .then((result) => { return result * 2; // 返回一个值 }) .then((finalResult) => { console.log(finalResult); // 使用上一个回调函数的返回值 });
如果
.then()
方法中的回调函数返回一个 Promise 对象,则返回的 Promise 对象的状态和值将取决于该返回的 Promise 对象。如果返回的 Promise 对象被解决(fulfilled),则后续的.then()
方法将接收到解决值;如果返回的 Promise 对象被拒绝(rejected),则后续的.catch()
或带有拒绝回调的.then()
方法将被触发。例如:jssomeAsyncFunction() .then((result) => { return new Promise((resolve, reject) => { resolve(result * 2); // 返回一个 Promise 对象 }); }) .then((finalResult) => { console.log(finalResult); // 使用上一个回调函数返回的 Promise 的解决值 });
注意:使用
resolve
方法将会返回一个已解决(fulfilled)状态的 Promise 对象,而使用reject
方法将会返回一个已拒绝(rejected)状态的 Promise 对象。resolve("Promise resolved");
reject(new Error("Promise rejected"));
如果
.then()
方法中的回调函数抛出一个异常,则返回的 Promise 对象将会处于被拒绝(rejected)状态,并且异常将作为拒绝理由传递给后续的.catch()
或带有拒绝回调的.then()
方法。例如:jssomeAsyncFunction() .then((result) => { throw new Error("Something went wrong"); // 抛出一个异常 }) .catch((error) => { console.log(error); // 捕获上一个回调函数抛出的异常 });
需要注意的是,.then()
方法返回的 Promise 对象是一个全新的 Promise 对象,它与前一个 Promise 对象是不同的。因此,在 Promise 链中可以进行多个 .then()
方法的串联调用,每个 .then()
方法都可以进行值的转换、异步操作或错误处理。
如果没有返回值呢,也没有使用 resolve 和 reject?还会继续链式调用吗?
如果前一个.then()
没有返回值(即没有明确的 return
语句),后续的.then()
仍然会被调用,但它们会接收到前一个.then()
中的 undefined
作为参数。
考虑以下示例:
promiseFunction()
.then((result) => {
console.log("First .then():", result); // 输出结果:First .then(): undefined
})
.then((result) => {
console.log("Second .then():", result); // 输出结果:Second .then(): undefined
});
在这个示例中,如果 promiseFunction()
返回一个 Promise,并且在第一个.then()
中没有明确的返回值,那么第二个.then()
会被调用,但它会收到 undefined
作为参数。如果在第一个.then()
中有返回值,那么第二个.then()
将会收到这个返回值作为参数。
那么 promise 的状态会是什么?
如果在 .then()
中没有返回值(或者显式地返回了 undefined
),Promise 的状态仍然会保持为已解决(fulfilled)状态,但是 Promise 内部的值会变成 undefined
。
这是因为在 JavaScript 的 Promise 链中,如果 .then()
或 .catch()
中没有返回任何值,那么会隐式地返回一个 Promise,该 Promise 的状态为已解决(fulfilled)状态,并且值为 undefined
。
总结:如果在 Promise 链的 .then()
中没有明确的返回值,Promise 的状态仍然是已解决(fulfilled),但内部的值会变成 undefined
。
为什么非要隐式地返回一个 fulfilled 状态的 undefined 呢?
这是因为,只有当 Promise 对象被解决(fulfilled)时,才会触发该 Promise 对象对应的 .then()
方法中的回调函数,Promise 的每个状态对应着不同的处理方法(.then、.catch),每个状态是通过特定的状态流转方法(resolve、reject)得来的,有着严格的对应关系!
如果连续的链式.then 时,其中有一个.then 在执行过程中发生了错误,那么后面的.then 还会 执行吗?
如果在连续的链式 .then()
方法中的某个回调函数发生了错误(抛出异常),则后续的 .then()
方法会被跳过,直接进入最近的 .catch()
方法(如果有的话)来处理错误。
这是因为在 Promise 链式调用中,每个 .then()
方法返回的是一个新的 Promise 对象,用于处理前一个 最近的状态符合的 Promise 对象的解决值。如果前一个 .then()
方法中的回调函数发生错误,它会导致该 Promise 对象变为已拒绝状态,而后续的 .then()
方法会被跳过。
直接 return 和使用
resolve
方法产生的效果是基本一样的
在某种程度上可以说返回一个值和使用 resolve
方法的效果是类似的,因为它们都会将一个值作为 Promise 对象的解决值进行传递,并且都是 fulfilled 状态的。
在大多数情况下,使用隐式的 return
语句是更常见和推荐的方式,因为它更简洁和直观。而手动创建 Promise 对象并使用 resolve
方法则更适用于需要进行更复杂操作或需要手动处理异步操作的情况。
注意:即便没有任何 return,也会隐式地返回一个 fulfilled 状态的值为 undefined 的 Promise 对象!
一个使用 redux-promise 中间件解决异步 action 时的例子:
export const incrementAsyncAction = (data, delay) => {
return axios({ url: xxx, method: "get", headers: {} }).then((res) => {
return incrementAction(res.data); //内部返回一个同步 action 对象,会被封装为一个 Promise 对象,并且状态为fulfilled(已解决),还可以继续.then
//如果使用 resolve方法 那么同样返回的是一个fulfilled的Promise对象
});
};
注意:上述 incrementAsyncAction 方法返回的是一个 fulfilled 的 Promise 对象
Promise 链式调用是基于 Promise 对象的状态和值,而不是依赖于回调函数的返回值
可以这样理解:在 Promise 链式调用中,每个 .then()
方法接收一个回调函数,而返回的是一个新的 Promise 对象,该对象的状态和值取决于前一个 Promise 对象的状态和前一个回调函数的返回值。
回调函数的返回值在 Promise 链中的作用是影响下一个 .then()
方法的输入值(即解决值)以及 Promise 的状态。
注意,当 Promise 对象被解决(fulfilled)时,会触发该 Promise 对象对应的 .then()
方法中的回调函数。而回调函数的返回值(无论有无)并不会直接影响 Promise 对象的状态,无非是不会走不会执行.then()
方法罢了!
要正确理解 Promise 链式调用,需要将重点放在 Promise 对象的状态和值的变化上,而回调函数的返回值仅仅是影响下一个 Promise 对象的输入值(解决值)而已。
总结:resolve 和 reject 方法用于状态的流转,then 和 catch 方法用于状态的接收!
.then 的第二个参数,以及.catch 的深入理解
.catch 后面如果还有.then 也是可以被执行的
// 函数A只返回一个reject异常
function A() {
return Promise.reject(new Error(Math.random()));
}
// 这样会先执行catch,然后执行后面的then
A()
.then(() => console.log("第一个then"))
.catch((e) => console.error(e))
.then(() => console.log("第二个then"));
原因:catch 也会隐式地返回一个值为 undefined 的 promise(fulfilled 状态的)所以可以继续 then。 从语义
上也很好理解,都被捕获处理过了就不是 error 了,所以可以继续!
如果我想如果报错就捕获 catch,并且不执行后面的 then 的话要怎么做呢?
1.改变顺序使用.then.catch
,then 里面要是报错也会被 catch 捕获,把 catch 放到最后
2.使用 try-catch,任意 Promise 中的 catch 异常,都会阻断后面代码的执行,并跳转到 try 中的 catch,被捕获
try {
A()
.then(() => console.log("第一个then"))
.then(() => console.log("第二个then"));
} catch (e) {
console.error(e);
}
如果 catch 后面有 catch,会执行吗?
可能会执行,是有条件的:需要显示地 reject 才可以,因为默认返回的是 fulfilled 状态的对象,只能被 then 捕获!
function B() {
return Promise.resolve("success!");
}
B()
.then((result) => {
console.log(result); // 打印前一个回调函数的返回值
})
.catch((error) => {
return "catch Error"; //返回的是值为undefined的fulfilled状态的Promise对象,只能被then捕获!
})
.catch((error) => console.log(error.message)); //不会被执行!
