AJAX
异步
如果能直接拿到将结果
那就是同步
比如你在医院挂号,你拿到号才会离开窗口
同步任务可能消耗 10 毫秒,也可能需要 3 秒
总之不拿到结果你是不会离开的,这就是同步
如果不能直接拿到结果
那就异步
比如你在餐厅门口等位,你拿到号后可以去干别的事比如逛街
你可以每 10 分钟取餐厅问一下排到自己了没(轮询)
也可以扫码用微信接受通知(回调)
异步举例
以 AJAX 为例
request.send()
之后,并不能直接得到 response用
console.log(request.response)
试试必须等到 readyState 变为 4 后,浏览器才会回头调用
request.onreadystatechange
函数我们才能得到
request.response
这就跟餐厅给你发微信提醒的过程类似
回调 callback
你写给自己用的函数,不是回调
你写给别人用的函数,就是回调
request.onreadystatechange
就是写个浏览器调用的意思是你(浏览器)回头调用一下这个函数
简单来理解就是:同步按你的代码顺序执行,异步不按照代码顺序执行,异步的执行效率更高。
1 | getJSON.onclick = () => { |
回调
写了却不调用,给别人调用的函数,就是回调。需自行意会
函数例子:
1 | function f1() {} |
分析:
- 我调用 f1 没有?
- 我把 f1 传给 f2 (别人)了没有?
- f2 调用 f1 了没有?
答:1. 调用了。2.穿了。3.f2 调用了 f1。
那么,f1 是不是我写给 f2 调用的函数? 是。
所以,f1 是回调。
例子2:
1 | function f1(x) { |
fn(‘hello’) 中的 fn 就是 f1 。
fn(’hello‘) 中的 ’hello’ 会被赋值给参数 x 。
所以 x 就是 ‘hello’。
异步和回调的关系
关联
异步任务需要再得到结果时通知 JS 来拿结果
怎么通知?
可以让 JS 留一个函数地址给浏览器(电话号码)
异步任务完成时浏览器调用该函数即可(拨打电话)
同时把任务作为参数传给该函数(通知)
这个函数是我写给浏览器调用的,所以是回调函数
区别
异步任务需要用到回调函数来通知结果
但回调函数不一定只用在异步任务里
回调可以用到同步任务里
array.forEach(n=>console.log(n))
就是同步回调
判断同步异步
如果一个函数的返回值处于
- setTimeout
- AJAX(即 XMLHttpRequest)
- AddEventListener
这三个东西内部中,那么这个函数就是异步函数
还有其他 API 是异步的,遇到再说。
举例说明:
摇骰子, 随机打印出1-6 中的一个数
1 | function 摇骰子() { |
分析:
摇骰子()
里没有写 return,那就是 return undefined
箭头函数里有 return,返回真正结果
所以这是一个异步函数/异步任务。
1 | const n = 摇骰子(); |
如何拿到异步结果?
用回调,写个函数,然后把函数地址给他
1 | function f1(x) { |
简化为箭头函数
1 | function f1(x) { |
1 | // 面试题 |
异步总结
- 异步任务不能拿到结果
- 于是我们传一个回调给异步任务
- 异步任务完成时调用回调
- 调用的时候把结果作为参数
异步任务两个结果,成功或失败
两个方法解决
方法一:回调接受两个参数
1 | fs.readFile('./1.txt', (error, data) => { |
方法二:两个回调
1 | ajax('get', '/1.json', data => {/*成功回调*/}, error => {/*失败回调*/}) |
这些方法的不足
不管方法一还是方法二,都有问题
- 不规范,名称五花八门,有人用 success + error,有人用 success + fail,done + fail
- 容易出现==回调地狱== ,代码变得看不懂
- 很难进行错无处理
回调地狱举例
1 | getUser( user => { |
如何解决回调问题,用 Promise
有什么办法能解决这三个问题:
- 会犯回调的名字或顺序
- 拒绝回调地狱,让代码可读性更强
- 很方便地捕获错误
1976年,Daniel P.Friedman 和 David Wis 俩人提出 Promise 思想
后人基于此发明了 Future、Delay、Deferred等
前端结合 Promise 和 JS,制定了 Promise/A+规范
该规范详细描述了 Promise 的原理和使用方法。
以 AJAX 的封装为例,来解释 Promise
1 | // 示例 |
Promise 写法:
1 | // 先改一下调用姿势 |
请问 ajax() 返回了个啥 ?
返回了一个含有 .then() 方法的对象呗。
那么再请问如何得到这个含有 .then() 的对象呢 ?
那就要改造 ajax 的源码了。
return new Promise((resolve, reject) => {})
1 | // 修改 ajax |
小结
第一步:
retrun new Promise((resolve, reject) => {...})
- 任务成功则调用
resolve(result)
- 任务失败则调用
reject(error)
- resolve 和 reject 会再去调用成功和失败函数
第二步:
- 使用
.then(success, fail)
传入成功函数和失败函数
我们自己封装的 ajax 的缺点:
post 无法上传数据。request.send(这里可以上传数据)
不能设置请求头。request.setRequestHeader(key, value)
使用:
axios(推荐)