异步编程:调用后耗时,不阻塞代码继续执行,将来完成后,触发回调函数传递结果
异步编程的范畴:
- 网络请求(如使用
fetch
或XMLHttpRequest
发送 HTTP 请求获取数据)。- 文件读写操作(读取或写入本地文件)。
- 数据库操作(查询、插入、更新、删除数据)。
- 定时器函数(如
setTimeout
和setInterval
)。- 事件处理(例如用户的点击、鼠标移动等事件)。
console.log('1 -----> ', 1)
setTimeout(() => {
console.log('2 -----> ', 2)
}, 100)
console.log('3 -----> ', 3)
setTimeout(() => {
console.log('4 -----> ', 4)
}, 0)
console.log('5 -----> ', 5)
猜一下 ,控制台打印出来的是啥 ??
基本概念
首先,我们需要明白async/await
和Promise都是JavaScript中处理异步操作的API。
- Promise: 是一种代表了某个异步操作最终完成(或失败)及其结果值的对象。
- async/await: 是基于Promise的语法糖,它允许我们以更同步的方式编写异步代码。
Promises(承诺):
承诺:承诺就像是一座桥梁🌉 连接着你和别人,或者连接着现在的你和未来的你。
它是你对别人的一种保证,也是你对自己的一种约束。当你做出承诺时,就像是在说:“我会尽我所能,让这件事成真!”,但是我们无法保证他一定会是真的。就比如说,
承诺十月底份减肥十斤------------------------------------------------------------------>
Promise
那现在承诺的状态就是进行中,因为十月份还没有过--------------------------->
pending
假设十一月份到了,我确实瘦了十斤,那么这个承诺就是成功的------------>
fulfilled
没有瘦十斤,那么承诺就是失败的--------------------------------------------------->
rejected
Promise
是 JavaScript 中处理异步操作的一种常见方式。Promise
有三种状态:
pending
(进行中):初始状态,既没有被兑现,也没有被拒绝
fulfilled
(已成功):意味着操作成功完成
rejected
(已失败): 意味着操作失败
状态有一些特性:
- 只能通过执行函数修改
- 外部无法读取
- 外部无法修改
1.创建一个 Promise
对象:
const myPromise = new Promise((resolve, reject) => {
// 异步操作
if (/* 异步操作成功的条件 */) {
resolve('成功的值'); // 成功时调用 resolve 并传递结果
} else {
reject('失败的原因'); // 失败时调用 reject 并传递错误信息
}
});
-
使用
then
方法处理成功的结果:myPromise.then(result => { console.log(result); // 处理成功返回的结果 });
-
使用
catch
方法处理失败的情况:// error 需要指定类型,ArkTS推断不出来 myPromise.catch((error:string) => { console.error(error); // 处理失败时的错误信息 });
- 举个例子
- 定义了一个名为
asyncOperation
的函数,该函数返回一个Promise
对象,并且明确指定这个Promise
成功时解析的值的类型为字符串。function asyncOperation(): Promise<string> { return new Promise<string>((resolve, reject) => { // 模拟异步操作 setTimeout(() => { if (Math.random() > 0.5) { resolve('操作成功'); } else { reject('操作失败'); } }, 1000); }); } asyncOperation() .then(result => console.log(result)) //随机数大于0.5返回操作成功 .catch((error:string) => console.error(error)); //随机数小于0.5返回操作失败
- 调用
asyncOperation
函数来执行其中的异步操作。- 使用
then
方法来处理Promise
成功时的情况。当异步操作成功(即随机数大于 0.5),resolve
被调用,then
中的回调函数会被执行,将成功的结果(字符串'操作成功'
)传递给result
参数,并通过console.log
打印出来。- 使用
catch
方法来处理Promise
失败时的情况。当异步操作失败(即随机数小于 0.5),reject
被调用,catch
中的回调函数会被执行,将失败的信息(字符串'操作失败'
)传递给error
参数,并通过console.error
打印错误信息。
2. Promise的链式写法
:
Promise
的链式写法允许在一个 Promise
的 then
方法中返回另一个 Promise
,从而形成链式的调用
基本写法:
new Promise((resolve, reject) => {
// 异步操作
if (/* 成功条件 */) {
resolve('成功的值');
} else {
reject('失败的原因');
}
})
.then(result => {
// 对成功结果进行处理,返回一个新的 Promise 或值
return new Promise((resolve, reject) => {
// 新的异步操作或处理逻辑
if (/* 新的成功条件 */) {
resolve('新的成功结果');
} else {
reject('新的失败原因');
}
});
})
.then(result => {
// 继续处理上一个 then 返回的结果
// 可以继续返回新的 Promise 以形成更长的链
})
.catch(error => {
// 处理任何一个环节的失败情况
console.error(error);
});
在链式写法中:
- 每个
then
方法接收上一个then
或resolve
传递的值,并可以返回一个新的Promise
或者直接返回一个值。 catch
方法用于捕获整个链中任何一个环节出现的错误。
举个例子
function createRandomPromise(delay: number, successMessage: string, failureMessage: string): Promise<string> { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.5) { resolve(successMessage); } else { reject(failureMessage); } }, delay); }); } createRandomPromise(1000, '操作成功', '操作失败') .then(result => { console.log(result); return createRandomPromise(1500, '第二次操作成功', '第二次操作失败'); }) .then(result => { console.log(result); return createRandomPromise(2000, '第三次操作成功', '第三次操作失败'); }) .then(result => { console.log(result); }) .catch((error: string) => { console.error(error); });
成功
fulfilled
失败
rejected
这种方式可以有效地处理异步操作,但当涉及到多个异步操作时,代码可能会变得复杂和难以阅读,这通常被称为“回调地狱”。
3. Promise 的静态方法:
Promise.all([promises])
:
- 接受一个可迭代对象(如数组),其中的元素都是
Promise
实例。当所有的Promise
都成功时,返回一个新的Promise
,其结果是一个数组,包含所有输入Promise
的成功结果,按照输入的顺序排列。const promise1 = Promise.resolve(3); const promise2 = 42; const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 5000, 'foo'); }) as Promise<string>; //这里调换了23的顺序 Promise.all([promise1, promise3,promise2]) .then((values) => { console.log(values.toString())}) .catch((error:string) => { console.log(error)}); //全是成功的,所以打印出来的数据 3 foo 42
- 快速失败机制:如果有任何一个
Promise
失败,则整个Promise.all
失败,返回的Promise
会立即被拒绝,并返回第一个失败Promise
的错误原因。 (不管有几个Promise,只要有一个失败了,其他的Promise就不会在执行了)const promise1 = Promise.resolve(3); const promise2 = 42; const promise3 = new Promise((resolve, reject) => { setTimeout(reject, 5000, 'foo'); }) as Promise<string>; Promise.all([promise1, promise3,promise2]) .then((values) => { console.log(values.toString())}) .catch((error:string) => { console.log(error)}); //因为3是失败的,所以只返回了 foo
Promise.allSettled([promises])
:
- 接受一个可迭代对象,其中的元素都是
Promise
实例。 - 返回一个新的
Promise
,当所有输入的Promise
都已完成(不管是成功完成还是失败完成)时,这个新的Promise
完成。其结果是一个数组,每个元素都描述对应的输入Promise
的完成状态和结果(如果成功则是值,如果失败则是错误原因)。const promise1 = Promise.resolve(3); const promise2 = new Promise((resolve, reject) => reject('出错啦!')) as Promise<string>; Promise.allSettled([promise1, promise2]).then((results) => { results.forEach((result) => { if (result.status === 'fulfilled') { console.log(result.value.toString()); } else { console.error(result.reason); } }); }); //返回了 3 和 出错啦!
Promise.race()
:
- 接受一个可迭代对象,其中的元素都是
Promise
实例。 - 返回一个新的
Promise
,只要输入的Promise
中有一个完成(不管是成功完成还是失败完成),就采用第一个完成的Promise
的结果。const promise1 = new Promise((resolve, reject) => { setTimeout(resolve, 500, 'one'); }) as Promise<string>; const promise2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 'two'); }) as Promise<string>; const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 1, 'three'); }) as Promise<string>; Promise.race([promise1, promise2,promise3]) .then((value) => { console.log(value); }) .catch((error: string) => { console.log(error); })
Promise.resolve()
:
- 将给定的值转换为一个已解决(resolved)的
Promise
。如果给定的值本身就是一个Promise
,则直接返回它。const resolvedPromise = Promise.resolve('直接成功'); resolvedPromise.then((value) => { console.log(value); });
Promise.reject()
:
- 创建一个已拒绝(rejected)的
Promise
,并带有指定的拒绝原因。
const rejectedPromise = Promise.reject('直接失败');
rejectedPromise.catch((reason:string) => {
console.error(reason);
});
async函数和 await
async/await
是一种非常强大和直观的方法。它是在ES2017中引入的,使得异步代码的阅读和编写更像是传统的同步代码,提高了代码的可读性和可维护性。
async
关键字用于声明一个异步函数,而await
关键字则用于等待一个Promise的解决(fulfill)或拒绝(reject)。
async
函数:
- 一个函数前面加上
async
关键字,就变成了async
函数。 async
函数总是返回一个Promise
。- 异步函数可以包含
await
表达式,以等待其他异步操作完成。async function fetchUserData(userId: number) { const response = await http .createHttp() .request(`https://example.com/api/user/${userId}`) ; const data = response.result as User; return data; }
await
关键字:
await
只能在async
函数内部使用,它用于等待一个Promise解决为其值。await
将暂停函数的执行,直到等待的Promise完成(无论成功或失败)并返回结果。- 如果
Promise
被拒绝(即抛出错误),那么await
表达式会抛出异常,需要在async
函数中使用try/catch
块来处理错误。async function getData() { try { const response = await http.createHttp().request('https://example.com/data'); const data = response.result; return data; } catch (error) { console.error('获取数据时出错:', error); return null; } }
await关键字其实很简单,js运行在碰到await关键字时,会记录在哪里暂停执行。等到await右边的值可以使用了,就是处理完回调了,js会向消息列对中推送一个任务,这个任务会恢复异步函数的执行。这样的话,即使await后面跟着一个立即可用的值,函数的其余部分也会被异步求值。
async function fun01(){ console.log(await Promise.resolve('第一名')); } async function fun02(){ console.log(await '第二名'); } async function fun03(){ console.log('第三名'); } fun01(); fun02(); fun03();
打印结果是 3 1 2
-
注意事项
- await不能单独使用,必须和async配对使用
- async要加在离await最近的函数,表示一对
- async修饰的函数,返回值类型会自动变为Promise<T>
- await只能拿到成功的结果,无法拿到失败的结果
-
try-catch可以用来捕获任意的异常,并不仅仅局限于 async 函数