在 Promise
构造函数中,确实通常会传入一个带有两个参数的回调函数,这两个参数分别是 resolve
和 reject
。resolve
用于将 Promise
状态从 pending
(进行中)变为 fulfilled
(已成功),reject
则用于将其状态从 pending
变为 rejected
(已失败)。
在之前给出的示例中,只使用了 resolve
而没有使用 reject
,这是因为示例的目的是模拟一个只会成功不会失败的异步操作。不过在实际开发里,很多异步操作是可能会失败的,所以需要使用 reject
来处理错误情况。
下面是一个同时使用 resolve
和 reject
的示例:
javascript
example.ts
Apply
function asyncOperationWithError(): Promise<void> {
return new Promise((resolve, reject) => {
// 模拟一个可能失败的异步操作,这里用 Math.random 来随机决定是否成功
const randomNumber = Math.random();
setTimeout(() => {
if (randomNumber < 0.5) {
console.log('异步操作成功');
resolve();
} else {
console.log('异步操作失败');
reject(new Error('异步操作失败'));
}
}, 2000);
});
}
async function main() {
console.log('开始异步操作');
try {
await asyncOperationWithError();
console.log('异步操作已成功结束');
} catch (error) {
console.error('异步操作出错:', error);
}
}
main();
代码解释
-
asyncOperationWithError
函数:- 创建了一个
Promise
,其回调函数接收resolve
和reject
两个参数。 - 使用
Math.random()
生成一个 0 到 1 之间的随机数,如果这个数小于 0.5,就认为异步操作成功,调用resolve()
;否则认为操作失败,调用reject()
并传入一个Error
对象。
- 创建了一个
-
main
函数:- 这是一个异步函数,使用
try...catch
块来处理asyncOperationWithError
可能抛出的错误。 - 如果
asyncOperationWithError
成功(Promise
状态变为fulfilled
),则打印异步操作已成功结束
;如果失败(Promise
状态变为rejected
),则在catch
块中捕获错误并打印错误信息。
- 这是一个异步函数,使用
所以,当你处理可能失败的异步操作时,通常要同时传入 resolve
和 reject
来分别处理成功和失败的情况。
以下是一个有返回值的 Promise
示例,在这个示例中,我们会模拟一个异步的数据库查询操作,该操作会返回查询到的数据。
javascript
example.ts
Apply
// 模拟数据库查询,返回一个 Promise,该 Promise 会在 2 秒后返回查询结果
function queryDatabase(): Promise<string[]> {
return new Promise((resolve, reject) => {
// 模拟 2 秒的查询延迟
setTimeout(() => {
const success = Math.random() < 0.8;
if (success) {
// 模拟查询成功,返回一个字符串数组作为查询结果
const result: string[] = ['data1', 'data2', 'data3'];
resolve(result);
} else {
// 模拟查询失败,返回一个错误信息
reject(new Error('数据库查询失败'));
}
}, 2000);
});
}
// 调用数据库查询函数,并处理结果
async function main() {
console.log('开始查询数据库');
try {
const data = await queryDatabase();
console.log('查询成功,结果如下:', data);
} catch (error) {
console.error('查询出错:', error);
}
}
// 启动主函数
main();
代码解释
-
queryDatabase
函数:- 该函数返回类型为
Promise<string[]>
,表示它会返回一个Promise
,当这个Promise
成功解决时,会返回一个字符串数组。 - 在
Promise
的回调函数中,使用setTimeout
模拟 2 秒的查询延迟。 - 通过
Math.random() < 0.8
来模拟查询成功的概率(80% 的成功率)。 - 如果成功,使用
resolve(result)
将查询结果(一个字符串数组)传递出去;如果失败,使用reject(new Error('数据库查询失败'))
抛出一个错误。**
- 该函数返回类型为
-
main
函数:- 这是一个异步函数,使用
await
关键字来等待queryDatabase
函数返回的Promise
被解决。 - 使用
try...catch
块来处理可能出现的错误。如果查询成功,将查询结果打印到控制台;如果查询失败,捕获错误并打印错误信息。
- 这是一个异步函数,使用
通过这种方式,我们可以处理有返回值的异步操作,并对可能出现的错误进行捕获和处理。
以下是一个包含两个 Promise
嵌套,且第二个 Promise
接收第一个 Promise
结果的 TypeScript 示例:
typescript
example.ts
Apply
// 第一个 Promise 函数,模拟异步获取用户 ID
function getUserID(): Promise<number> {
return new Promise((resolve, reject) => {
// 模拟异步操作,如网络请求
setTimeout(() => {
const success = Math.random() < 0.8;
if (success) {
// 模拟成功获取用户 ID
const userID = 123;
resolve(userID);
} else {
// 模拟获取失败
reject(new Error('获取用户 ID 失败'));
}
}, 1500);
});
}
// 第二个 Promise 函数,接收用户 ID 并模拟异步获取用户信息
function getUserInfo(userID: number): Promise<{ name: string; age: number }> {
return new Promise((resolve, reject) => {
// 模拟异步操作,如根据用户 ID 从数据库获取用户信息
setTimeout(() => {
const success = Math.random() < 0.8;
if (success) {
// 模拟成功获取用户信息
const userInfo = { name: 'John Doe', age: 30 };
resolve(userInfo);
} else {
// 模拟获取失败
reject(new Error(`获取用户 ID 为 ${userID} 的信息失败`));
}
}, 1500);
});
}
// 主函数,调用上述两个 Promise 函数
async function main() {
try {
console.log('开始获取用户 ID...');
const userID = await getUserID();
console.log(`成功获取用户 ID: ${userID},开始获取用户信息...`);
const userInfo = await getUserInfo(userID);
console.log('成功获取用户信息:', userInfo);
} catch (error) {
console.error('操作出错:', error);
}
}
// 启动主函数
main();
代码解释:
-
getUserID
函数:- 该函数返回一个
Promise<number>
,表示会返回一个Promise
,当Promise
成功时,会返回一个number
类型的用户 ID。 - 使用
setTimeout
模拟一个异步操作,有 80% 的概率成功获取用户 ID,若成功则调用resolve
并传入用户 ID;若失败则调用reject
并传入错误信息。
- 该函数返回一个
-
getUserInfo
函数:- 该函数接收一个
number
类型的userID
参数,返回一个Promise<{ name: string; age: number }>
,表示返回的Promise
成功时会得到一个包含用户姓名和年龄的对象。 - 同样使用
setTimeout
模拟异步操作,根据传入的userID
去获取用户信息,有 80% 的概率成功,成功时调用resolve
并传入用户信息对象,失败时调用reject
并传入错误信息。
- 该函数接收一个
-
main
函数:- 这是一个异步函数,使用
await
关键字来按顺序执行getUserID
和getUserInfo
函数。 - 先调用
getUserID
函数获取用户 ID,若成功则接着使用该用户 ID 调用getUserInfo
函数获取用户信息。 - 使用
try...catch
块捕获可能出现的错误并打印错误信息。
- 这是一个异步函数,使用
通过这种方式,实现了两个 Promise
的嵌套,并且第二个 Promise
接收了第一个 Promise
的结果。
以下是使用老式的 Promise
链式调用方式(即将第二个 Promise
写在第一个 Promise
的 resolve
回调里)来实现相同功能的示例代码:
typescript
example.ts
Apply
// 第一个 Promise 函数,模拟异步获取用户 ID
function getUserID(): Promise<number> {
return new Promise((resolve, reject) => {
// 模拟异步操作,如网络请求
setTimeout(() => {
const success = Math.random() < 0.8;
if (success) {
// 模拟成功获取用户 ID
const userID = 123;
resolve(userID);
} else {
// 模拟获取失败
reject(new Error('获取用户 ID 失败'));
}
}, 1500);
});
}
// 第二个 Promise 函数,接收用户 ID 并模拟异步获取用户信息
function getUserInfo(userID: number): Promise<{ name: string; age: number }> {
return new Promise((resolve, reject) => {
// 模拟异步操作,如根据用户 ID 从数据库获取用户信息
setTimeout(() => {
const success = Math.random() < 0.8;
if (success) {
// 模拟成功获取用户信息
const userInfo = { name: 'John Doe', age: 30 };
resolve(userInfo);
} else {
// 模拟获取失败
reject(new Error(`获取用户 ID 为 ${userID} 的信息失败`));
}
}, 1500);
});
}
// 主函数,调用上述两个 Promise 函数
function main() {
getUserID()
.then((userID) => {
console.log(`成功获取用户 ID: ${userID},开始获取用户信息...`);
return getUserInfo(userID);
})
.then((userInfo) => {
console.log('成功获取用户信息:', userInfo);
})
.catch((error) => {
console.error('操作出错:', error);
});
}
// 启动主函数
main();
代码解释:
-
getUserID
函数:- 此函数返回一个
Promise<number>
,使用setTimeout
模拟异步操作来获取用户 ID。以 80% 的概率成功获取用户 ID,成功时调用resolve
并传入用户 ID,失败时调用reject
并传入错误信息。
- 此函数返回一个
-
getUserInfo
函数:- 该函数接收一个
number
类型的userID
参数,返回一个Promise<{ name: string; age: number }>
。同样使用setTimeout
模拟根据用户 ID 获取用户信息的异步操作,以 80% 的概率成功获取,成功时调用resolve
并传入用户信息对象,失败时调用reject
并传入错误信息。
- 该函数接收一个
-
main
函数:- 调用
getUserID
函数,使用.then
方法处理其返回的Promise
。 - 在第一个
.then
回调中,接收getUserID
成功返回的用户 ID,打印相应信息,然后调用getUserInfo
函数并返回其返回的Promise
。 - 第二个
.then
回调用于处理getUserInfo
成功返回的用户信息并打印。 - 使用
.catch
方法捕获整个过程中可能出现的错误并打印错误信息。
- 调用
这种方式通过 Promise
的 .then
链式调用,将第二个 Promise
的调用放在第一个 Promise
成功后的回调里,实现了两个异步操作的顺序执行。不过这种方式在处理多个嵌套的 Promise
时,代码会变得难以阅读和维护,也就是所谓的"回调地狱"问题。而使用 async/await
语法可以让代码更加简洁易读。