异步与Promise
异步
能直接拿到结果(等待结果出现)
不能直接拿到结果(先运行别的,然后等结果到了被通知)
- 每过一段时间问一下(轮询)
- 通过微信扫码接受通知(回调)
异步举例
以ajax为例
- requeset.send()之后,并不能直接得到response
- 不信console.log(request.response)试试
- 必须等到readyState变为4以后,浏览器回头调用request。onreadystatechange函数
- 我们才能得到request.response
- 这跟餐厅给你发微信提示的过程是类似的
回调callback
- 你写给自己用的函数,不是回调
- 你写给别人用的函数,才是回调
- request.onreadystatechange就是我写给浏览器调用的
- 意思是你(浏览器)回头(将来)调用一下这个函数,不是马上调用
回调
写了却不调用,给别人调用的函数,就是回调
【回头你调用一下呗】
意会
回调举例
把函数1给另一个函数2
1 2 3 4 5 6 7 | function f1(str){ console.log(str) } function f2(fn){ fn( '你好我是来自f1的代码' ) <br> //此处的参数对应f1(str) } f2(f1) |
分析
- 我调用f1没有? 答:没有调用
- 我把f1传给f2(别人)了 没有?答:传了
- f2调用f1了没有?答:f2调用了f1
那么,f1是不是我写给f2调用的函数?答:是
所以,f1是回调
异步和回调的关系
关联
- 异步任务需要在得到结果时通知js来拿结果
- 怎么通知呢?
- 可以让js写一个函数地址(电话号码)给浏览器
- 异步任务完成时浏览器调用该函数地址即可(拨打电话)
- 同时把结果作为参数传给该函数(电话里通知)
- 这个函数是我写给浏览器调用的,所以是回调函数
区别
- 异步任务需要用到回调函数来通知结果
- 但回调函数不一定只用在异步任务里
- 回调可以用到同步任务里
- array.forEach(n=>console.log(n))就是同步回调
函数为同步还是异步?通过文档或者特征
判断同步异步
如果一个函数的返回值处于
- setTimeout
- AJAX(即XMLHttpRequest)
- AddEventListener
- 这三个东西内部,那么这个函数就是异步函数
ajax可以设置为同步,但是体验会很差,别去做。
简化箭头函数
由于f1声明之后只用了一次,所以可以删掉f1
1 2 3 4 5 6 7 | function f1(x){ console.log(x)} 摇骰子(f1) //改为如下 摇骰子(x=>{console.log(x)} //再简化 摇骰子(console.log) //但是一定要注意,参数个数必须一致 |
总结
- 异步任务不能拿到结果
- 浴室我们传了一个回调给异步任务
- 异步任务完成时调用回调
- 调用的时候把结果作为参数
- 希望你已经理解上面过程
一定要避免回调地狱,可读性和可维护性都很差。
怎么解决回调问题
有什么办法解决这三个问题
- 规范回调的名字和顺序
- 拒绝回调地狱,让代码可读性更强
- 很方便得捕获错误
根据前人经验提出的promise思想
用promise封装ajax
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ajax = (method,url,options)=>{ const {success,fail} = options //析构赋值 const request = new XMLHttpsRequest() request.open(method,url) request.onreadystatechange = ()=>{ if (request.readyState === 4){<br> //成功就调用resolve,失败就调用reject success.call( null ,request.response) } else if (request.status >= 400){ fail.call( null ,request,request.status) } } } request.send() } //调用 ajax( 'get' , '/xxx' ,{ success(response){},fail:(request,status)=>{ }) //左边是function缩写,右边是箭头函数,记下来 |
上面的代码比较傻,没用promise写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ajax = (method,url,options)=>{ return new Promise((resolve,reject)=>{ const {success,fail} = options //析构赋值 const request = new XMLHttpsRequest() request.open(method,url) request.onreadystatechange = ()=>{ if (request.readyState === 4){ //成功就调用resolve,失败就调用reject success.call( null ,request.response) } else if (request.status >= 400){ fail.call( null ,request,request.status) } } } request.send() }) } |
return new Promise((resolve,reject)=>{})
背下这五个单词即可,用熟再说
小结
第一步
- return new Promise((resolve,reject)=>{})
- 任务的成功则调用resolve(result)
- 任务失败则调用reject(error)
- resolve和reject会再去调用成功和失败的函数
第二步
- 使用.then(success,fail)传入成功和失败的函数
点到为止
- 先讲到这里,promise还有高级用法,以后再说
封装的ajax的缺点
- post无法上传数据
- 不能设置请求头
- 解决方案 1、花时间写到完美 2、使用jQuery.ajax 3、使用axios这个很实用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话