当前位置: 首页 > article >正文

ES6的简单介绍(第三部分)

五 异步编程

5.5 回调地狱

        回调地狱这个词听起来就非常的高大上,在接触Promise之前,必须要懂得什么是回调地狱,以及为什么会产生回调地狱?

        先来看看概念:当一个回调函数嵌套一个回调函数的时候就会出现一个嵌套结构,当嵌套的多了就会出现回调地狱的情况。

        根据前面我们可以得出一个结论:存在异步任务的代码,不能保证能按照顺序执行,那如果我们非要代码顺序执行呢?

举个例子:让四个回调函数打印骆宾王的《咏鹅》,咱们必须要这样操作,才能保证顺序正确:

setTimeout(function () {
   setTimeout(function () {
      setTimeout(function () {
         setTimeout(function () {
            console.log("红掌拨清波")
         }, 1000)
         console.log("白毛浮绿水")
      }, 2000)
      console.log("曲项向天歌")
   }, 3000)
   console.log("鹅鹅鹅")
}, 4000)

        可以看到,代码中的回调函数套回调函数,居然套了4层,这种回调函数中嵌套回调函数的情况就叫做回调地狱

        总结一下,回调地狱就是为实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。

那该如何解决回调地狱呢?

两种方式

1. 使用ES6引入的promise
2. async/await

5.6 promise的应用

        Promise是js中的一个原生对象(也可以理解为一个类或者一个构造函数),是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案

        Promise是一种对异步操作的封装,表示一个操作现在可能还没有完成,但将来会完成,并提供了成功或失败时要执行的操作。

Promise: ES6引入的一个新的概念(可以理解为是一个类,或者是一个构造函数)
        引入的目的主要是解决回调地狱的写法

        异步编程想要做到类似于同步变成的顺序执行,才会涉及到回调地狱

5.6.1 Promise的三种状态

Promise(承诺)的状态:   回调函数执行:从开始到结束。
pending: 等待状态:  承诺的这件事没有结果(结果只有两种情况,要么成功,要么失败),也可以理解为对象刚刚初始化完毕时的状态
fulfilled: 完成,成功状态:  承诺的这件事得到满意的结果。
rejected: 失败,拒绝状态:  承诺的这件事得到不满意的结果。比如类似ajax发送请求,结果收到了404,500这类的状态码

5.6.2 Promise的参数

        Promise构造函数的参数,需要我们传入一个函数,我们需要处理的异步任务就是书写在该函数体内。传入的这个函数,要求必须要有两个参数,第一个参数是异步任务执行成功时的回调函数,第二个参数是异步任务执行失败时的回调函数,正常来说,参数名叫什么都无所谓,但是咱们尽量做到见名知意嘛,所以规范中命名为resolve(解决的含义),reject(拒绝)异步任务执行成功时调用resolve函数返回结果,反之调用reject。

Promise构造函数的参数:
        1.参数是一个回调函数。(参数1,参数2)=>{......}
        2.回调函数里的第一个参数是一个函数,表示成功状态调用的函数,因此参数名可以做到见名知意,叫resolve(解决的含义)
        3.回调函数里的第二个参数也是一个函数,表示失败、拒绝状态调用的函数,因此参数名可以做到见名知意,叫reject(拒绝的含义) 

let p = new Promise( (resolve,reject)=>{
//     使用伪代码来模拟开启的一部编程的任务是否成功执行,或者是失败执行
    /*
        随机数为0~70: 表示任务执行成功
        随机数为71~100: 表示任务执行失败
     */
    let num1 = Math.round(Math.random()*100);
    console.log(num1);
    if (num1<=70){
        resolve("任务1执行成功");
    }else{
        reject("任务1执行失败了");
    }
});

 5.6.3 常用API

  • then()

Promise对象的then方法用来接收处理成功时响应的数据。

then(onfulfilled,onrejected): Promise对象调用该方法
        1. 第一个参数onfulfilled: 用于给Promise的回调函数(参数)的第一个参数resolve赋值,赋的值是一个函数
        函数的样子: (data)=>{......}  :任务执行的结果作为参数传递给data
        2. 第二个参数onrejected: 用于给Promise的回调函数的第二个参数reject赋值,赋的值也是一个函数。
        函数的样子: (reason)=>{......}  :任务执行失败的结果作为参数传递给reason
        作用:任务失败时,执行逻辑的封装。 可以代替catch。
        应用场景:每个人物的失败都像单独处理,可以用第二个参数。 

  •  catch()

catch方法用来接收处理失败时相应的数据

 catch(onrejected)方法:  任务失败时执行该方法,Promise对象调用该方法
        1.参数只有一个onrejected:也是一个函数,用于给Promise的回调函数(参数)的第二个参数reject赋值,赋的值也是一个函数。
        函数的样子: (reason)=>{......}
        作用:所有的任务在失败时想要统一处理时,使用catch。

    Promise的链式调用:
        p.then().then().then()...
        注意: then()方法是Promise对象的,因此,想要链式调用then方法,那么前一个then必须返回一个新的Promise对象

演示代码:

let p = new Promise( (resolve,reject)=>{
//     使用伪代码来模拟开启的一部编程的任务是否成功执行,或者是失败执行
    /*
        随机数为0~70: 表示任务执行成功
        随机数为71~100: 表示任务执行失败
     */
    let num1 = Math.round(Math.random()*100);
    console.log(num1);
    if (num1<=70){
        resolve("任务1执行成功");
    }else{
        reject("任务1执行失败了");
    }
});

// let f1 = (data)=>{
//     console.log("onfulfilled: "+data);
// }
// let f2 = (data)=>{
//     console.log("onrejected: "+data);
// }
// p.then(f1,f2);
p.then((data)=>{
    console.log("onfulfilled: "+data);
    //第二个任务: 必须在任务1成功执行后,执行
    return new Promise((resolve,reject)=>{
        let num2 = Math.round(Math.random()*100)+100;
        console.log(num2);
        if (num2<=170){
            resolve("任务2执行成功");
        }else{
            reject("任务2执行失败了");
        }
    })
},(reason)=>{
    console.log("失败原因1: "+reason);
    return new Promise((resolve,reject)=>{
        let num2 = Math.round(Math.random()*100)+100;
        console.log(num2);
        if (num2<=170){
            resolve("任务2执行成功");
        }else{
            reject("任务2执行失败了");
        }
    })
}).then((data)=>{
    console.log("第二个任务: "+data);
},(reason)=>{
    console.log("失败原因2: "+reason);
})
//     .catch((reason)=>{
//     console.log("失败原因: "+reason);
// })
//     .catch((reason)=>{
//     console.log("onrejected: "+reason);
// })

 Promise完成异步的顺序执行代码

//
// let p =new Promise((resolve,reject)=>{
//     resolve("鹅鹅鹅");
// })
//
// p.then((value)=>{
//     console.log(value);
//     return new Promise((resolve,reject)=>{
//         resolve("曲项向天歌");
//     })
// }).then((value)=>{
//     console.log(value);
//     return new Promise((resolve,reject)=>{
//         resolve("白毛浮绿水");
//     })
// }).then((value)=>{
//     console.log(value);
//     return new Promise((resolve,reject)=>{
//         resolve("红掌拨清波");
//     })
// }).then((value)=>{
//     console.log(value);
// })




let p1=new Promise((resolve,reject)=>{
    let num =Math.round(Math.random()*50);
    if (num<=30){
        resolve("鹅鹅鹅");
    }else{
        reject("锄禾日当午");
    }
})

p1.then((data)=>{
    console.log(data)
    return new Promise((resolve,reject)=>{
        let num2 =Math.round(Math.random()*50);
        if (num2<=30){
            resolve("曲项向天歌");
        }else{
            reject("汗滴禾下土");
        }
    })
},(value)=>{
    console.log(value);
    return new Promise((resolve,reject)=>{
        let num2 =Math.round(Math.random()*50);
        if (num2<=30){
            resolve("曲项向天歌");
        }else{
            reject("汗滴禾下土");
        }
    })
}).then((data)=>{
    console.log(data);
    return new Promise((resolve,reject)=>{
        let num2 =Math.round(Math.random()*50);
        if (num2<=30){
            resolve("白毛浮绿水");
        }else{
            reject("谁知盘中餐");
        }
    })
},(value)=>{
    console.log(value);
    return new Promise((resolve,reject)=>{
        let num2 =Math.round(Math.random()*50);
        if (num2<=30){
            resolve("白毛浮绿水");
        }else{
            reject("谁知盘中餐");
        }
    })
}).then((data)=>{
    console.log(data);
    return new Promise((resolve,reject)=>{
        let num2 =Math.round(Math.random()*50);
        if (num2<=30){
            resolve("红掌拨清波");
        }else{
            reject("粒粒皆辛苦");
        }
    })
},(value)=>{
    console.log(value);
    return new Promise((resolve,reject)=>{
        let num2 =Math.round(Math.random()*50);
        if (num2<=30){
            resolve("红掌拨清波");
        }else{
            reject("粒粒皆辛苦");
        }
    })
}).then((data)=>{
    console.log(data);
},(value)=>{
    console.log(value);
})

5.7 Fetch

Fetch: 是一个基于Promise的API,可以发送HTTP请求

第一种:发送get请求
        fetch(url).then((response)=>{...})
        解析:

        1.url: 请求路径,get请求需要将请求参数拼接到url上。
        2.第一个then里的匿名函数是用于处理请求成功后服务器响应的数据,形参resopnse是整个想要对象的封装,里面含有很多键值对的信息
        比如: type,url,status,redirected,ok, 已经服务端绑定的数据。
        3.如果想要获取服务端发送过来的数据,
                需要 return 响应对象上的json()  实际上返回的是一个新的Promise
        4.因此,如果想要继续处理数据,那么需要继续调用then方法。
        5. 如果处理的是异常信息,可以使用catch

演示代码:

let btn1 = document.querySelector("#btn1");
    // alert(btn1.nodeName);
    btn1.addEventListener("click", function (){
        fetch("checkLogin.lr?username=zhangsan").then((data)=>{
            console.log(data);
            return data.json();
        }).then((data)=>{
            console.log(data);
            // console.log(data.error_msg);
            document.querySelector("#s1").innerText = data.error_msg;
        })
    })

 第二种:发送post请求
        1.格式: fetch(url,{method:"post",body:"请求参数"})
        2.解析: url: 请求路径
                {}用于指定其他的参数
                method:请求方式,默认为get
                body:用于指定请求参数,使用对象类型的写法。还需要使用URLSearchParams类型进行包装
        如果method制定了get请求,那么就不需要body属性,而是将请求参数拼接到url上

演示代码 

 let btn2 = document.querySelector("#btn2");
    // alert(btn1.nodeName);
    btn2.addEventListener("click", function (){
        fetch("checkLogin.lr",
            {method:"post",
            headers:{'content-type':'application/x-www-form-urlencoded'},
            body:"username=zhangsan"
            }).then((data)=>{
            console.log(data)
            return data.json();
        }).then((jsonStr)=>{
            console.log(jsonStr.error_msg);
        })
    })

 第三种: 发送post请求,并且发送json格式的数据

演示代码 

let btn3 = document.querySelector("#btn3");
    let obj = {username:"lisi"};
    // alert(btn1.nodeName);
    btn3.addEventListener("click", function (){
        fetch("checkLogin.lr", {
                method:"post",
                // headers:{'content-type':'application/x-www-form-urlencoded'},
                body:JSON.stringify(obj)
            }).then((data)=>{
            console.log(data)
            return data.json();
        }).then((jsonStr)=>{
            console.log(jsonStr.error_msg);
        })
    })

5.8 Async/Await

        Async/await建立在Promises之上语法糖使得异步代码的编写和理解更接近传统的同步代码风格。(使用同步的方式编写异步代码)

5.8.1 Async关键字

声明函数时,在前面添加Async关键字,表示该函数为一个异步任务,不会阻塞后面函数的执行。

async function fn(){
   return '任务1';
}
console.log(fn());   // Promise{fulfilled: "任务1"}

        打印可以看到async函数返回数据时自动封装为一个Promise对象。

        和Promise对象一样,处理异步任务时也可以按照成功和失败来返回不同的数据,处理成功时用then方法来接收,失败时用catch方法来接收数据

虽然Promise可以解决回调地狱的问题,但是Promise变成充斥着大量的then函数。
        因此可以使用Async/Await来简化Promise的异步编程。
        Async/Await是建立在Promise之上的一个语法糖,可以使用同步代码的风格实现异步编程。

        Async: 异步的意思, 用法简单, 只需要在定义的函数前,添加async关键字即可。
        1. async声明的函数,会隐式返回一个Promise对象
        2. async声明的函数,还是使用then或者catch来调用
        3. 函数中如果正确使用return关键字进行了数据的返回,那么Promise的状态就是fulfilled。
        如果函数中的代码抛出了异常,那么Promise的状态就是rejected。

        4. async修改的函数,本质上是Promise,调用时还是异步代码,并不会阻塞后续的代码运行。
        5. 在普通函数前添加async关键字,等价于在普通函数中return 一个Promise对象

async function f1(){
    // console.log("任务一");
    try{
        return "任务一";
    }catch (e){
        throw new Error("任务一出错了")
    }
}
// function f1(){
//     return new Promise((resolve,reject)=>{
//         try{
//             //......省略大量代码
//             resolve("任务一")
//         }catch (e){
//             reject("任务一出错了")
//         }finally {
//             //释放资源
//         }
//
//     })
// }

f1().then((value)=>{
    console.log(value);
},(reason)=>{
    console.log(reason);
})
console.log("---我是主线程代码---")

5.8.2 Await关键字

  • await关键字只能在使用async定义的函数中使用

  • await后面可以直接跟一个 Promise实例对象(可以跟任何表达式,更多的是跟一个返回Promise对象的表达式)

  • await函数不能单独使用, await等待的意思,可以等待任何表达式,不过最常用的是等待一个Promise对象。

  • await可以直接拿到Promise中resolve中的数据。

  • await后续的代码什么时候才执行? 等到结果后,才会执行。 也就是await会阻塞后面的代码

测试代码: 

// 两秒后获得一个随机数
function getRandomNumber(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve(Math.round(Math.random()*3));
        },2000)
    })
}

// async function getRandomNumber(){
//     setTimeout(()=>{
//         return Math.round(Math.random()*3);
//     },2000)
// }

async function test(){
    // 2秒后,获取一个随机数
    let r1 = await getRandomNumber(); //等2秒,可以获取随机数,给变量r1
    let r2 = await getRandomNumber(); //继续等2秒,获取第二个随机数,给变量r2
    let r3 = await getRandomNumber(); // 继续等2秒,获取第三个随机数,给变量r3
    console.log(r1,r2,r3);
}
test();
console.log("---我是6秒前执行的,await关键字并不影响主线程中的代码执行。---")

5.9 Axios

5.9.1 Axios简介

Axios(中文谐音:爱克丝赛欧斯)

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

官方网站:http://www.axios-js.com

源码:https://gitee.com/charlinchenlin/store-pos

特性:

1、从浏览器中创建 XMLHttpRequests

2、从 node.js 创建 http 请求

3、支持 Promise API

4、拦截请求和响应

5、转换请求数据和响应数据

6、取消请求

7、自动转换 JSON 数据

8、客户端支持防御 XSRF

5.9.2 安装方式

 使用 npm:

$ npm install axios

 使用 bower:

$ bower install axios

 使用CDN

 <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

 5.9.3 案例演示

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>axios</title>
</head>
<body>
    <button id="btn1">axios发生get请求--方法</button>
    <button id="btn2">axios发生post请求--方法</button>
    <button id="btn3">axios发生get请求</button>
    <button id="btn4">axios发生post请求</button>
    <br><span id="s1" style="color: red"></span>
</body>
<script src="js/axios.js"></script>
<script>
    let btn1 = document.querySelector("#btn1");
    let btn2 = document.querySelector("#btn2");
    let btn3 = document.querySelector("#btn3");
    let btn4 = document.querySelector("#btn4");

    /*
        axios.get((url,{}))
        解析: url: 请求资源路径
              {}:用于指定其他参数的,比如请求参数,注意请求参数的key是固定写法,必须叫params
                    params的值,也只能是对象形式。
     */

    btn1.addEventListener("click",()=>{
        //模拟从文本框拿到数据后做了一个对象的封装
        let loginInfo = {"username":'zhangsan',password:123456}

        axios.get("checkLogin.lr",{params:loginInfo}).then((response)=>{
            // 因为response这个对象上有一个叫data的属性,该属性用于存储服务端发送过来的数据,因此直接使用data属性即可
            // console.log(response.data)
            let obj = response.data;
            document.querySelector("#s1").innerText=obj.error_msg;
        })
    })

    /**
     * 后端Servlet只能从getReader()里读取数据,getParameter(...)不行
     */
    // btn2.addEventListener("click",()=>{
    //     //模拟从文本框拿到数据后做了一个对象的封装
    //     let loginInfo = {"username":'zhangsan',password:123456};
    //     axios.post("checkLogin.lr",loginInfo).then((response)=>{
    //         // 因为response这个对象上有一个叫data的属性,该属性用于存储服务端发送过来的数据,因此直接使用data属性即可
    //         console.log(response.data)
    //         let obj = response.data;
    //         document.querySelector("#s1").innerText=obj.error_msg;
    //     })
    // })

    btn2.addEventListener("click",()=>{
        //模拟从文本框拿到数据后做了一个对象的封装
        let loginInfo = {"username":'zhangsan',password:123456};
        axios.post("checkLogin.lr","username=zhangsan&&password=123456").then((response)=>{
            // 因为response这个对象上有一个叫data的属性,该属性用于存储服务端发送过来的数据,因此直接使用data属性即可
            console.log(response.data)
            let obj = response.data;
            document.querySelector("#s1").innerText=obj.error_msg;
        })
    })

    /**
     * axios({
     *     url: 请求路径,
     *     method: 请求方式,默认为get,
     *     params/data: get请求时,使用params传请求参数,post请求时,必须使用data传请求参数
     *     headers: 可选,post请求时,必须设置
     * })
     */

    btn3.addEventListener("click",()=>{
        //模拟从文本框拿到数据后做了一个对象的封装
        let loginInfo = {"username":'zhangsan',password:123456};
        axios({
          url:"checkLogin.lr",
          method:"get",
          params:loginInfo
        }).then((response)=>{
            // 因为response这个对象上有一个叫data的属性,该属性用于存储服务端发送过来的数据,因此直接使用data属性即可
            console.log(response.data)
            let obj = response.data;
            document.querySelector("#s1").innerText=obj.error_msg;
        })
    })
    btn4.addEventListener("click",()=>{
        //模拟从文本框拿到数据后做了一个对象的封装
        let loginInfo = {"username":'zhangsan',password:123456};
        axios({
            url:"checkLogin.lr",
            method:"post",
            headers:{"content-type":"application/x-www-form-urlencoded"},
            data:loginInfo
        }).then((response)=>{
            // 因为response这个对象上有一个叫data的属性,该属性用于存储服务端发送过来的数据,因此直接使用data属性即可
            console.log(response.data)
            let obj = response.data;
            document.querySelector("#s1").innerText=obj.error_msg;
        }).catch((error)=>{
            console.log(error);
        })
    })

</script>
</html>


http://www.kler.cn/a/320302.html

相关文章:

  • 优化 MFC CGridCtrl 的表格布局与功能
  • 反向代理模块
  • AWD脚本编写_1
  • 模式:每个服务一个数据库
  • Centos使用人大金仓ksql
  • IPTV智慧云桌面,后台服务器搭建笔记
  • AR传送门+特定区域显示内容+放大镜 效果着色器使用
  • 文件上传漏洞+CTF实例
  • 时频分析法——连续小波变换(CWT)
  • ubuntu数据硬盘故障导致系统启动失败
  • 四元组问题
  • 医院伤员小程序点餐———未来之窗行业应用跨平台架构
  • C# 游戏引擎中的协程
  • Dubbo快速入门(一):分布式相关概念
  • python学习记录3
  • 专业学习|《随机过程》学习笔记(二)(定义、分类及相关过程)
  • 虚幻引擎第三人称和第一人称任意切换
  • 图论系列(dfs)9.25
  • Xk8s证书续期
  • 从文本图片到多模态:3D 数字人打开企业全域商业增长新空间
  • ROS1是DCS,ROS2是FCS
  • LangChain教程 - 基于SQL数据的问答系统教程
  • 吉林一号宽幅01星亚米级宽幅相机影像的无控定位精度
  • Linux——应用层协议HTTP
  • Altium Designer(AD)百度云下载与安装(附安装步骤)
  • VMware 如何上网