异步、回调与promise
Contents
异步与同步
在理解promise
之前,我们首先要搞清楚,什么是异步?什么是回调?
网上的解释经常混淆异步与回调。
一句话解释
如果能直接拿到结果,就是同步。
例如在医院窗口挂号,拿到号你才会离开窗口。同步任务可能消耗10ms,也可能需要3s。总之不拿到结果你是不会离开的。
如果不能直接拿到结果,那就是异步。
比如你在餐厅门口等位,你拿到号可以去逛街。什么时候才能真正吃饭呢?
你可以每10分钟去餐厅问一下,这种方法就是轮询。
也可以扫码用微信接受通知,而这就是我们所说的回调。
以AJAX
为例
request.send()
之后,并不能直接得到response
。必须等到readyState
变为4
后,浏览器回头调用request.onreadystatechange
函数,我们才能得到request.response
。
这跟餐厅给你发送微信提醒的过程是类似的。
回调callback
你写给自己用的函数,不是回调。
你写给别人用的函数,就是回调。
request.onreadystatechange
就是你写给浏览器调用的,意思就是让浏览器回头调用一个这个函数。
写了却不调用,给别人调用的函数,就是回调。“回头你调用一下呗”。
举例
把函数1给另一个函数2
|
|
分析
- 我调用f1没有?答:没有
- 我把f1传给f2(别人)了没有?答:传了
- f2调用f1了没有?答:f2调用了f1
综上,f1是我写给f2调用的函数,f1是回调。
异步与回调的关系
异步任务需要用到回调函数,但是回调函数不一定只用在异步任务里,也可以用到同步任务里。
array,forEach(n⇒console.log(n))
就是同步回调
异步任务需要在得到结果时通知JS来拿结果。怎么通知呢?
可以让JS写留一个函数地址(电话号码)给浏览器,异步任务完成时浏览器调用该函数地址即可(拨打电话)。同时把结果作为参数传给该函数(电话通知可以来吃了)
这个函数是我写给浏览器调用的,所以是回调函数。
判断异步同步
如果一个函数的返回值处于
- setTimeout
- AJAX(即XMLHttpRequest)
- AddEventListener
这三个东西内部,那么这个函数就是异步函数。
注意,不要把AJAX设置为同步,这样会使请求期间页面卡住。
摇骰子
|
|
分析
摇骰子()没有写return,那就是return undefined
箭头函数里有return,返回真正的结果
所以这是一个异步函数/异步任务
摇骰子续
|
|
那怎么拿到异步结果呢?
可以用回调,写个函数,然后把函数地址给它。
|
|
然后我要求摇骰子函数得到结果后把结果作为参数传给f1
|
|
这个代码可以进行简化
由于f1声明之后只用了一次,所以可以删掉f1
|
|
总结
- 异步任务不能直接立刻拿到结果
- 于是我们传一个回调给异步任务
- 异步任务完成时调用回调
- 调用的时候把结果作为参数
但是异步任务有两个结果,如果失败,怎么办?
方法一:回调接受两个参数,node.js就是用这种方法
|
|
方法二:搞两个回调
|
|
这些方法的不足
不管是方法一还是方法二,都有问题
- 不规范,名称五花八门,有人用success+error,有人用success+fail,有人用done+fail
- 容易出现回调地狱,代码变得看不懂
- 很难进行错误处理
回调地狱举例
|
|
这还只是四层回调,你能想象20层回调吗?
怎么解决回调问题?怎么让回调变得更好用?
Promise
为了实现这三个目标
- 规范回调的名字或顺序
- 拒绝回调地狱,让代码可读性更强
- 很方便地捕获错误
前端程序员开始翻书了
- 1976年,Daniel P.Fridman和David Wise提出Promise思想
- 后人基于此发明了Future、Delay 、Deferred等
- 前端结合Promise和JS,制订了Promise/A+规范,该规范详细描述了Promise的原理和使用方法
以AJAX的封装为例来解释Promise的用法
|
|
Promise说这代码太傻了,我们改成Promise写法
|
|
虽然也是回调,但是不需要记success和fail了 then的第一个参数就是success,then的第二个参数就是fail
请问ajax()返回了个啥?
返回了一个含有.then()方法的对象呗
那么再请问如何得到这个含有.then()的对象呢?
那就要改造ajax的源码了
|
|
return new Promise((resolve,reject)⇒{})
小结
- return new Promise((resolve,reject)⇒{…})
- 任务成功调用resolve(result),任务失败调用reject(error)
- resolve和reject会再去调用成功和失败函数
- 使用.then(success,fail)传入成功和失败函数
Author wuyining
LastMod 2022-01-04