异步与回调的区别
同步
-
如果能直接拿到结果
-
那就是同步
-
比如你在医院挂号,你拿到号才会离开窗口
-
同步任务可能消耗 10 毫秒,也可能需要 3 秒
-
总之不拿到结果你是不会离开的
异步
-
如果不能直接拿到结果
-
那就是异步
-
比如你在餐厅门口等位,你拿到号可以去逛街
-
什么时候才能真正吃饭呢?
-
你可以每10分钟去餐厅问一下(轮询)
-
你也可以扫码用微信接收通知(回调)
异步举例
- 以AJAX为例:
request.send()之后,并不能直接得到response,必须等到readyState变为4后,浏览器回头调用request.onreadystatechange函数,我们才能得到request.response,这跟餐厅给你发送微信提醒的过程是类似的。
回调 callback
-
你写给自己用的函数,不是回调
-
你写给别人用的函数,就是回调
-
request.onreadystatechange 就是我写给浏览器调用的
-
意思就是你(浏览器)回头调一下这个函数,在中文里,「回头」也有「将来」的意思,如「我回头请你吃饭」。
-
写了却不调用,给别人调用的函数,就是回调
异步和回调的关系
关联
-
异步任务需要在得到结果时通知 JS 来拿结果
-
怎么通知呢?
-
可以让 JS 留一个函数地址(电话号码)给浏览器
-
异步任务完成时浏览器调用该函数地址即可(拨打电话)
-
同时把结果作为参数传给该函数(电话里说可以来吃了)
-
这个函数是写给浏览器调用的,所以是回调函数
区别
-
异步任务需要用到回调函数来通知结果
-
但回调函数不一定只用在异步任务里
-
回调可以用到同步任务里
-
array.forEach( n => console.log(n) )就是同步回调
小试牛刀
- 关于异步,正确的有
-
如果 JS 不能直接拿到一个函数的结果,可以先去执行别的代码,等结果到了再取结果,这就是异步
-
异步的结果可以通过轮询获取,轮询就是定时去询问结果拿到了没有
-
异步的结果可以通过回调获取,一般来说结果会被作为回调的第一个参数
-
异步的好处是可以把用来等待的时间拿去做别的事情
- 关于回调,正确的有
-
满足某些条件的函数才被称为回调,比如我写一个函数 A,传给另一个函数 B 调用,那么函数 A 就是回调
-
回调可以用于同步任务,不一定非要用于异步任务(只需要加一个false)
- 有的时候回调还可以传给一个对象,如 request.onreadystatechange,等待浏览器来调用
判断同步异步
-
如果一个函数的返回值处于
-
setTimeout -
AJAX(即 XMLHttpRequest) -
AddEventListener -
这三个东西内部,那么这个函数就是异步函数
总结
-
异步任务不能拿到结果
-
于是我们传一个回调给异步任务
-
异步任务完成时调用回调
-
调用的时候把结果作为参数
如果异步任务有两个结果成功或失败,怎么办?
这些方法的不足(重点记忆3个问题)
- 不管方法一还是方法二,都有问题
-
不规范,名称五花八门,有人用 success + error,有人用 success + fail,有人用 done + fail
-
容易出现回调地狱,代码变得看不懂
-
很难进行错误处理
- 回调地狱举例
getUser( user => {
getGroups(user, (groups)=>{
groups.forEach( (g)=>{
g.filter(x => x.ownerId === user.id)
.forEach(x => console.log(x))
})
})
})
怎么解决回调问题
有什么办法能解决这三个问题
-
规范回调的名字或顺序
-
拒绝回调地狱,让代码可读性更强
-
很方便地捕获错误
前端程序员开始翻书了
-
1976 年,Daniel P. Friedman 和 David Wise
-
俩人提出 Promise 思想
-
后人基于此发明了 Future、Delay、Deferred 等
-
前端结合 Promise 和 JS,制订了 Promise/A+ 规范
-
该规范详细描述了 Promise 的原理和使用方法
Promise
-
Promise对象表示异步操作的最终完成(或失败)及其结果值。 -
从本质上讲,
promise是一个返回的对象,您将回调附加到该对象,而不是将回调传递给函数。
用Promise改写一下代码
-
注意:
Promise.reject()方法返回一个Promise因给定原因而被拒绝的对象。Promise.resolve()方法返回一个Promise使用给定值解析的对象。 -
重点记忆:
return new Promise((resolve, reject)=>{}) -
关于
return new Promise((resolve, reject)=>{...})中的 resolve 和 reject,正确的有
-
resolve 和 reject 可以改成任何其他名字,不影响使用,但一般就叫这两个名字
-
任务成功的时候调用 resolve,失败的时候调用 reject
-
resolve 和 reject 都只接受一个参数
-
resolve 和 reject 并不是 .then(succes, fail) 里面的 success 和 fail,resolve 会去调用 success,reject 会去调用 fail
小结
第一步
-
return new Promise((resolve,reject)=>{...}) -
任务成功则调用
resolve(result) -
任务失败则调用
reject(error) -
resolve和reject会再去调用成功和失败函数
第二步
-
使用
.then(success, fail)传入成功和失败函数 -
该**
then()** 方法返回一个Promise ., 它最多需要两个参数:. 的成功和失败情况的回调函数Promise。
小试牛刀
- 关于 Promise,正确的有
-
Promise 不是前端发明的
-
Promise 是目前前端解决异步问题的统一方案
-
window.Promise 是一个全局函数,可以用来构造 Promise 对象
-
使用 return new Promise((resolve, reject)=> {}) 就可以构造一个 Promise 对象
-
构造出来的 Promise 对象含有一个 .then() 函数属性
封装的 ajax 的缺点
post 无法上传数据
request.send(这里可以上传数据)
不能设置请求头
request.setRequestHeader(key, value)
怎么解决呢?
-
花时间把 ajax 写到完美(有时间可以做)
-
使用 jQuery.ajax(这个可以)
-
使用
axios(这个库比 jQuery 逼格高)
jQuery.ajax
封装优点
-
支持更多形式的参数
-
支持 Promise
-
支持的功能超多
axios
目前最新的 AJAX 库
- 它抄袭了 jQuery 的封装思路
代码示例
axios.get('/5.json')
.then( response =>
console.log(response)
)
- 关于 axios,正确的有
-
这是一个专门用于操作 AJAX 的库
-
axios.get('/xxx') 返回一个 Promise 对象
-
axios.get('/xxx').then(s, f) 在请求成功的时候调用 s,失败的使用调用 f
axios 高级用法
JSON 自动处理
-
axios 如何发现响应的 Content-Type 是 json
-
就会自动调用 JSON.parse
-
所以说正确设置 Content-Type 是好习惯
请求拦截器
- 你可以在所有请求里加些东西,比如加查询参数
响应拦截器
-
你可以在所有响应里加些东西,甚至改内容
-
可以生成不同实例(对象)
-
不同的实例可以设置不同的配置,用于复杂场景
总结
-
异步是什么
-
异步为什么会用到回调
-
回调哪三个问题:地域、名字、错误处理
-
Promise 是什么:1976 年的一种设计模式
-
如何使用 Promise:背下来五个词
-
如何使用 Axios:发个请求试用看看
-
Promise 是前端解决异步问题的统一方案