33·灵魂前端工程师养成-[前后分离]异步与promise

2022-10-31 18:10:04 浏览数 (1)

  • JS异步编程模型

  • AJAX封装
  • Promise写法
  • axios

-曾老湿, 江湖人称曾老大。


-多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。 -擅长Web集群架构与自动化运维,曾负责国内某大型金融公司运维工作。 -devops项目经理兼DBA。 -开发过一套自动化运维平台(功能如下): 1)整合了各个公有云API,自主创建云主机。 2)ELK自动化收集日志功能。 3)Saltstack自动化运维统一配置管理工具。 4)Git、Jenkins自动化代码上线及自动化测试平台。 5)堡垒机,连接Linux、Windows平台及日志审计。 6)SQL执行及审批流程。 7)慢查询日志分析web界面。


JS异步编程模型


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

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

代码语言:javascript复制
## 如果能直接拿到结果,那就是同步
# 例如:
# 1.在医院挂号,你拿到号才会离开
# 2.QQ的在线发送文件,对方发送完,你接收完文件才能关闭QQ


## 如果不能直接拿到结果,那就是异步
# 例如:
# 1.你在餐厅门口等位,叫号,你拿到号可以去逛街
# 那么什么时候才能去吃饭呢?
# 你可以每隔几分钟回去问一次,还剩几个。(轮询)
# 你也可以扫码用微信接收通知。(回调)
# 2.QQ的离线发送文件。

以上是生活中的例子,那么我们接下来以代码为例子。

代码语言:javascript复制
## 以AJAX为例
# request.send()之后,并不能直接得到respone
# 不信,可以console.log(request.respone)试试
# 必须等到readyState变为4后,浏览器回头调用request.onreadystatechange函数
# 我们才能得到request.response
# 这跟餐厅给你发微信提醒的过程类似


## 回调callback
# 你写给自己用的函数,不是回调
# 你写给别人用的函数,就是回调
# request.onreadystatechange就是我写给浏览器调用的
# 意思就是,你(浏览器)回头调用一下这个函数。
# 在中文中「回头」也有「将来」的意思,如:「我回头请你吃饭」

异步和回调的关系

代码语言:javascript复制
## 关联
# 异步任务需要在得到结果时通知JS来拿结果
# 怎么通知呢?
# 可以让JS写留一个函数地址(电话号码)给浏览器
# 异步任务完成时,浏览器调用该含税地址即可(拨打电话)
# 同时把 结果作为参数传给该函数(电话说,可以来吃饭了)
# 这个函数是我写给浏览器调用的,所以是回调函数

## 区别
# 异步任务需要用到回调函数来通知结果。
# 但回调函数不一定只用在异步任务里
# 回调可以用到同步任务里
# array.forEach(n=>console.log(n))就是同步回调

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

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

1.setTimeout 2.AJAX(即XMLHTTPRequest) 3.AddEventListener

求都嘛得~ 我听说AJAX可以设置为同步的。

傻X前端才会把AJAX设置为同步,这样做会使请求期间页面卡住。

我们试一下把之前代码改成同步,整个页面都会卡主。

代码语言:javascript复制
getJSON.onclick = () =>{
    const request = new XMLHttpRequest()
    request.open('GET','/zls.json',false)
    request.onreadystatechange = () =>{
        if(request.readyState === 4 ){
            if(request.status >= 200 && request.status < 300){
                console.log(request.response)
                const object = JSON.parse(request.response)
                myName.textContent = object.name
            }
        }
    }
    request.send()
}

我点了两个请求,先请求json再请求xml,会发现,必须等到json请求返回之后,才会发起对xml的请求。

我们改回异步试试。

代码语言:javascript复制
getJSON.onclick = () =>{
    const request = new XMLHttpRequest()
    request.open('GET','/zls.json')
    request.onreadystatechange = () =>{
        if(request.readyState === 4 ){
            if(request.status >= 200 && request.status < 300){
                console.log(request.response)
                const object = JSON.parse(request.response)
                myName.textContent = object.name
            }
        }
    }
    request.send()
}

两个请求可以同时触发,异步返回结果。


如果异步任务有两个结果怎么办?

当我们的异步结果有两个,一个是成功,一个是失败,我们该怎么做?

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

代码语言:javascript复制
fs.readFile('./1.txt',(error,data)=>{
    if(error){console.log('失败');return}
    console.log(data.toString())//成功
})

方法二:搞两个回调呗

代码语言:javascript复制
ajax('GET','/1.json',data()=>{},error()=>{})
//前面函数是成功回调,后面函数是失败回调
ajax('GET','/1.json',{
    success:()=>{},fail:()=>{}
})
//接受一个对象,对象有两个key,表示成功和失败

但是...特么的,只要有但是,那么但是之前的都是废话,上面两种方法都不行。

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

1.不规范,名称五花八门,因为是约定,有人用success error ,有人用success fail,有人用done fail

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

3.很难进行错误处理

回调地狱举例:

代码语言:javascript复制
getUser(user=>{
    getGroups(user,(group)=>{
        groups.forEach((g)=>{
            g.filter(x => x.ownerId === user.id).forEach(x => console.log(x))
        })
    })
})

这还只是4层回调,你能想象20层回调么?

网上吐槽回调地狱的图片【HaDoGen】

如何解决上述的三个问题呢?

1.规范回调的名字或顺序 2.拒绝回调地狱,让代码可读性更强 3.很方便的捕获错误


前端程序猿开始翻书了

  • 1976年,Daniel P.Friedman 和 David Wise。
  • 俩人提出了Promise思想。
  • 后人基于此发明了Future、Delay、Deferred等。
  • 前端结合Promise和JS,制定了Promise/A 规范。
  • 该规范详细描述了Promise的原理和使用方法。

AJAX封装

代码语言:javascript复制
ajax = (method,url,options)=>{
    const {success,fail} = options //析构赋值
    const request = new XMLHttpRequest()
    request.open(method,url)
    request.onreadystatechange = () =>{
        if(request.readyState === 4){
            //成功就调用 success,失败就用 fail
            if(request.status < 400){
                success.call(null,request.respone)
            }else if(request.status >= 400){
                fail.call(null,request,request.status)
            }
        }
    }
    request.send()
}

// 调用封装函数
ajax('get','/xxx',{
    success(response){},fail:(request,status)=>{}
})

//左边是function缩写,右边是箭头函数,记下来,别再问我

Promise写法

Promise说,上面这个代码太傻了,我们使用Promise写法吧。因为之前我们说的那三个原因,不规范、回调地狱、很难错误处理。

代码语言:javascript复制
// 先改一下调用姿势
ajax('get','/xxx',{
    success(respone){},fail:(request,status)=>{}
})

// 上面代码,用到了两个回调,还是用了 success 和 fail

// 改成Promise写法
ajax('get','/xxx').then((respone)=>{},(request)=>{})

// 虽然也是回调
// 但是不用记success和fail了
// then的第一个参数就是success
// then的第二个参数就是fail

// 请问ajax()返回了个啥?
// 返回了一个含有.then()方法的对象呗
// 那么再请问,如何得到这和个含有.then()的对象呢?
// 那就要改造ajax的源码了,如下:

ajax = (method,url,options)=>{
    return new Promise((resolve,reject)=>{
        const {suuccess,fail} = options
        const request = new XMLHttpRequest()
        request.open(method,url)
        request.onreadystatechange = () =>{
            if(request.readyState === 4){
                if(request.status < 400){
                    resolve.call(null,request.respone)
                }else if(request.status >= 400){
                    reject.call(null,request)
                }
            }
        }
        request.send()
    })
}

背下来下面五个单词即可,等你用熟了,自然就掌握了Promise。

代码语言:javascript复制
return new Promise((resolve,reject)=>{}

小结:

第一步: return new Promise((resolve,reject)=>{...}) 任务成功则调用resolve(result) 任务失败则调用reject(error)

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

点到为止: Promise先介绍到这里,高级用法,我们后面再说,先把五个单词背会


封装AJAX的缺点

1.post无法上传数据 request.send(这里可以上传数据)

2.不能设置请求头 request.setRequestHeader(key,value)

怎么解决呢? 花时间把ajax写到完美(有时间可以做) 使用JQuery.ajax(这个可以) 使用axios(这个库比JQuery逼格高)


JQuery.ajax

已经非常完美,进入JQuery文档,搜索ajax找到jQuery.ajax,看看参数说明,然后直接看代码示例,看看jQuery的封装,就知道自己的封装有多么的辣鸡了。

封装的优点: 支持更多形式的参数 支持Promise 支持的功能超多

我们需要掌握jQuery.ajax嘛? 不用,现在的专业前端都在用axios 写篇博客,罗列一下功能,就可以忘掉jQuery了。

axios

jQuery.ajax我们可以忘了,目前,最新的AJAX库,axios划重点,显然它抄袭了jQuery的封装思路。如果有人问我你记得axios的API吗?对不起,不记得,但是我写了博客...

代码示例:

代码语言:javascript复制
axios.get('/5.json')
    .then(respone =>
        console.log(response)
    )

axios高级用法

JSON自动处理 axios如果发现响应的Content-Type是json,就会自动 调用JSON.parse,所以说,正确设置Content-Type是好习惯。

请求拦截器 你可以 在所有请求里加东西,比如加查询参数

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

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

0 人点赞