题目⑦ js是如何实现异步编程的?
如何实现异步io?
1.回调函数callback
-
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等待,会拖延整个程序的执行)
-
缺点:回调地狱,不能用
try catch
捕获错误,不能return
2.Promise
-
优点:解决了回调地狱的问题
-
缺点:无法取消
Promise
,错误需要通过回调函数来捕获
3.async/await
-
优点:代码清晰,不像
Promise
写一堆then
链,处理了回调地狱的问题 -
缺点:
await
将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用await
会导致性能降低
3.1async/await
如何通过同步的方式实现异步?
async/await
是参照generator
封装的一套异步处理方案,可以理解为generator
的语法糖,而generator
又依赖于迭代器Iterator
,Iterator
的思想又源于单向链表
3.1.1 单向链表
-
链表的优点
-
无需预先分配内存
-
插入/删除节点不影响其他节点,效率高
-
-
单向链表
是链表中最简单的一种,包含两个域:信息域与指针域,这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。
-
特点:
-
节点的链接方向是单向的
-
相对于数组来说,单链表的随机访问速度较慢,但单链表删除/添加数据的效率很高
-
-
3.1.2 Iterator
- Iterator迭代器 的遍历过程类似于单向链表
3.1.3 Generator
- Generator:生成器对象时生成器函数返回的,它符合可迭代协议和迭代器协议,既是迭代器也是可迭代对象,可以调用 next 方法,但它不是函数,更不是构造函数
本质:暂停
它会让程序执行到指定位置先暂停 (yield),然后再启动(next),再暂停(yield),再启动(next),而这个暂停就很容易让它和异步操作产生联系
-
处理异步时
-
开始异步处理(网络请求、IO操作)
-
然后暂停一下
-
处理完,再该干嘛干嘛
-
-
js是单线程的,异步还是异步,
callback
还是callback
,不会因为generator
而有任何改变
3.1.4 async/await
- async/await是 generator 的语法糖,就是一个自执行的 generate函数。利用 generator函数的特性把异步的代码写成同步的形式
async/await
和promise
的用法?
1. async/await
的用法
-
async/await
是函数定义的关键字 -
await
用于等待promise对象
的返回结果,且不能单独使用必须放在async函数
中 -
利用
async
定义的函数会返回一个promise
对象 -
async
函数的返回值就是promise
状态为resolved
的返回值
2. promise
promise
是一个构造函数,有all
reject
resolve
这几个方法,原型上有then
catch
等方法
2.1 特点
① 对象的状态不受外界影响
-
promise对象
代表一个异步操作,有三种状态-
pending
(进行中) -
fulfilled
(已成功) -
rejected
(已失败)
-
② 一旦状态改变就不会再变,任何时候都可以得到这个结果
-
promise对象
的状态改变,只有两种可能-
从
pending
变为fulfilled
-
从
pending
变为rejected
-
2.2 promise
的用法
① 首先new
一个promise
let p = new Promise(function(resolve, reject) {
// do sth async
setTimeout(function() {
console.log('执行完成promise')
resolve('要返回的数据')
}, 2000)
})
// 执行完成promise
② new
一个promise
对象,不需要调用就执行了
③ 使用promise
的时候一般是包在一个函数中,在需要的时候去运行这个函数
const clickFunc = () => {
console.log('点击方法被调用')
return new Promise(function(resolve, reject) {
// do sth async
setTimeout(function() {
console.log('执行完成promise')
resolve('要返回的数据')
}, 2000)
})
}
为什么要放在函数里面
- 函数
return
出promise
对象,执行这个函数我们可以得到一个promise
对象。接下来就可以用promise
对象上的then
catch
方法了
resolve
是什么
promise
只是能够简化层层回调的写法,实质上,promise
的精髓是 状态,用维护状态、传递状态的方式使得回调函数能够及时调用
reject
的用法
then
方法可以接受两个参数,第一个对应resolved
的回调,第二个对应reject
的回调,并且能在回调函数中拿到成功的数据和失败的原因
function promiseClick(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
promiseClick().then(
function(data){
console.log('resolved成功回调');
console.log('成功回调接受的值:',data);
},
function(reason){
console.log('rejected失败回调');
console.log('失败执行回调抛出失败原因:',reason);
}
);
catch的用法
-
作用一:用来捕获异常,与
then
方法中接受的第二参数rejected
的回调一样 -
作用二:再执行
resolved
的回调时,如果抛出异常,并不会报错卡死js,而是会进入catch
方法中
all的用法
- 该方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后并且执行结果都是成功的时候才执行回调
function promiseClick1(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
function promiseClick2(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
function promiseClick3(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
Promise
.all([promiseClick3(), promiseClick2(), promiseClick1()])
.then(function(results){
console.log(results);
});
race的用法
-
谁先执行完成就先执行回调
-
先执行完的不管是进行了
race
的成功回调还是失败回调,其余的将不会再进入race
的任何回调
function promiseClick1(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
function promiseClick2(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})?
}
function promiseClick3(){
return new Promise(function(resolve, reject){
setTimeout(function(){
var num = Math.ceil(Math.random()*20); //生成1-10的随机数
console.log('随机数生成的值:',num)
if(num<=10){
resolve(num);
}
else{
reject('数字太于10了即将执行失败回调');
}
}, 2000);
})
}
Promise
.race([promiseClick3(), promiseClick2(), promiseClick1()])
.then(function(results){
console.log(results);
},function(reason){
console.log('失败',reason);
});
async/await
和promise
用法的区别?举例两三个?
1. promise
是ES6
,async/await
是ES7
async await
是基于Promise
实现的,可以说是改良版的Promise
,它不能用于普通的回调函数
2. async/await
相对于promise
来讲,写法更加简洁
-
Promise
的出现解决了传统callback函数
导致的“地域回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。 -
而
async await
代码看起来会简洁些,使得异步代码看起来像同步代码,await
的本质是可以提供等同于“同步效果”的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句
3. reject
状态:
-
promise
错误可以通过catch
来捕捉,建议尾部捕获错误, -
async/await
既可以用.then
又可以用try-catch
捕捉
async/await
和promise
性能的区别?
async/await
优点
-
它做到了真正的串行的同步写法,代码阅读相对容易
-
对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面
-
处理复杂流程时,在代码清晰度方面有优势
async/await
缺点
-
无法处理
promise
返回的reject
对象,要借助try...catch...
-
await
只能串行,做不到并行await
做不到并行,不代表async
不能并行,只要await
不在同一个async
函数里就可以并行
-
try...catch...
内部的变量无法传递给下一个try...catch...
-
async/await
无法简单实现Promise
的各种原生方法,比如.race()
之类
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)