41. 异步与Promise

Promise

什么是异步?什么是同步?

网上的解释经常混淆异步与回调

什么是同步?

如果能直接拿到结果就是同步。

举例:

比如你在医院挂号,你拿到号才会离开窗口。

同步任务可能消耗10毫秒,也可以需要3秒,总之不拿到结果你是不会离开的。

什么是异步?

如果不能直接拿到结果就是异步。

举例:

比如你在餐厅门口等位,你拿到号可以去逛街。

什么时候才能真正吃饭呢?

你可以每10分钟去餐厅问一下(轮询)。

你也可以扫码用微信接受通知。

异步举例

以AJAX为例

request.send()之后,并不能直接得到response,不信的话可以用console.log(request.response)试试。必须等到readyState变为4后,浏览器回头调用request.onReadystatechange函数,我们才能得到request.response,这跟餐厅给你发送微信提醒的过程是类似的。

回调callback

你写给自己用的函数,不是回调,你写给别人用的函数,就是回调。request.onreadystatechange就是我写给浏览器调用的意思就是你(浏览器)回头调一下这个函数。回头也有将来的意思,就是在将来的某一个时刻,调用下这个函数。

回调

什么是回调?

写了却不调用,给别人调用的函数,就是回调。(回头你调用一下呗)

回调举例1

把函数1给另一个函数2

分析

我调用f1没有?答:没有

我把f1传给f2(别人)了没有?答:传了

f2调用f1了没有?f2调用了f1

那么,f1是不是我写给f2调用的函数,答:是

所以,f1是回调。

回调举例2

f1是回调函数,被传给f2,f1作为回调函数接受'你好'作为参数。

异步与回调的关系

关联

异步任务需要在得到结果时通知JS来拿结果,可以让JS写留一个函数地址(电话号码)给浏览器。异步任务完成时浏览器调用该函数地址即可(拨打电话)。同时把结果作为参数传给该函数(电话里说可以来吃了)。这个函数时我写给浏览器调用的,所以是回调函数。

区别

异步常常用到回调,但是不一定要用到回调。

异步任务需要用到回调函数来通知结果(也可以用到轮询)

调函数不一定只用在异步任务里

回调可以用到同步任务里,比如,array.forEach(n => console.log(n))就是同步回调。

如何判断一个函数是同步还是异步

很简单,跟据特征或者文档。

判断同步异步

如果一个函数的返回值处于下面三种情况,那么这个函数就是异步函数。

  • setTimeout

  • AJAX(即XMLHttpRequest)

  • AddEventListener

问:我听说AJAX可以设置为同步的

答:啥x前端才把AJAX设置为同步的,这样做会使请求期间页面卡住。

AJAX也可以做同步请求,只需要添加false即可。

request.open("get", "/5.json", false)

看下面图中绿色的bar,当把AJAX设置成同步之后,会等上一个请求完成才开始执行,同时页面也会出现卡顿。浏览器知道用户点击了,但是不会有任何反馈。

截屏2023-04-16 下午6.30.45

举例(什么样的代码是异步的?)

摇骰子

分析

摇骰子()没有写return,那就是return undefined

箭头函数里有return,返回真正的结果,所以这是一个异步函数/异步任务。

如何拿到异步结果?

答:可以用回调。写个函数,然后把函数地址给它。然后,要求摇骰子函数得到结果后把结果作为参数传给f1。

简化为箭头函数

由于f1声明之后只用了一次,所以可以删掉f1

著名的面试题

⚠️注意:

总结

异步任务不能拿到结果

于是我们传一个回调给异步任务

异步任务完成时调用回调

调用的时候把结果作为参数

希望你已经理解上面的过程。

如果异步任务有两个结果成功或失败,怎么办

两个结果

方法一:回调接受两个参数

方法二:搞两个回调

这些方法的不足

不管方法一还是方法二,都有问题

面试官会问,为什么要用到promise?以下就是答案

  1. 不规范,名称五花八门,有人用success + error, 有人用success + fail, 有人用done + fail

  2. 容易出现回调地狱,代码变得看不懂

  3. 很难进行错误处理

回调地狱举例

这还只是四层回调,你能想象20层回调吗?

Callback Functions & Callback Hell - DEV Community

怎么解决回调问题

有什么办法能解决这三个问题

  • 规范回调的名字或顺序

  • 拒绝回调地狱,让代码可读性更强

  • 很方便地捕获错误

前端程序员开始翻书了

1976年,Daniel P. Friedman和David Wise两人提出了Promise思想。后人基于此发明了Future,Delay,Deferred等。

前端结合Promise和JS,制定了Promise/A+规范。该规范详细描述了Promise的原理和使用方法。

以AJAX的封装为例来解释Promise的用法

Promise说这代码太傻了,我们改成Promise写法

完整代码

return new Promise((resolve, reject) =>{})背下这五个单词即可,等你用熟了。。。

小结

  • 第一步

    return new Promise ((resolve,reject)=>{...})

    任务成功则调用 resolve(result)

    任务失败则调用 reject(error)

    resolve 和reject 会再去调用成功和失败函数

  • 第二步

    使用.then(success, fail) 传入成功和失败函数

  • 点到为止

    先讲到这里,Promise 还有高级用法,以后说

我们封装的 ajax 的缺点

  • post 无法上传数据

    request.send(这里可以上传数据)

  • 不能设置请求头

    request.setRequestHeader(key, value)

  • 怎么解决呢?

    花时间把 ajax 写到完美(有时间可以做)

    使用 jQuery.ajax(这个可以)

    使用 axios(这个库比 jQuery逼格高)

jQuery.ajax

  • 已经非常完美

    进入jQuery 的文档,搜索 ajax,找到jQuery.ajax

    看看参数说明,然后直接看代码示例

    看看jQuery 的封装,就知道自己的封装是辣鸡了

    中文文档:地址

  • 封装优点

    支持更多形式的参数

    支持 Promise

    支持的功能超多

  • 我们需要掌握 jQuery.ajax 吗?

    不用,现在的专业前端都在用 axios

    写篇博客罗列一下功能,就可以忘掉jQuery了

axios

  • 目前最新的 AJAX库

    显然它抄袭了 jQuery 的封装思路

    方方记得 axios 的API 吗?

    不记得,但是我写了博客

    通过这个博客我们可以快速了解 axios 的用法

    推荐大家也可以通过写博客来学习一个库

  • 代码示例

axios 高级用法

  • JSON 自动处理

    axios 如何发现响应的 Content-Type 是json

    就会自动调用 JSON.parse

    所以说正确设置 Content-Type 是好习惯

  • 请求拦截器

    你可以在所有请求里加些东西,比如加查询参数.

  • 响应拦截器

    你可以在所有响应里加些东西,甚至改内容

    比如说发一个请求,但是后端的api还没有写好,就可以通过响应拦截器来篡改

    链接

  • 可以生成不同实例(对象)

    不同的实例可以设置不同的配置,用于复杂场景

封装!封装!封装! 初级程序员学习 API(包括 vue / React 的 API) 中级程序员学习如何封装 高级程序员造轮子

课后作业

Promise问答题

你需要自行查看Promise的MDN文档才能答对

Axios问答题

你需要自行查看axios的文档才能答对

我们的项目会经常用到这俩玩意

所以忘了也没关系

总结

Promise的最大缺点就是不可以取消,所以axios发明了axios.CancelToken,本质就是编个号。

  • 异步是什么?

    • 不能直接拿到结果的就叫异步

  • 异步为什么会用到回调?

    • 需要用回调拿到不能直接拿到的结果

  • 回调有哪三个问题?

    • 回调地狱,名字不规范,错误处理

  • Promise是什么?

    • 1976年的一种设计模式

  • 如何使用Promise?背下来五个词

    • return new Promise((resolve,reject)=>{})

  • 如果使用Axios:发个请求试用看看

  • Promise是前端解决异步问题的统一方案

最后更新于

这有帮助吗?