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

五 异步编程

5.5 回调地狱

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

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

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

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

javascript 复制代码
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(拒绝的含义)

javascript 复制代码
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

  1. 第二个参数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对象

演示代码:

javascript 复制代码
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完成异步的顺序执行代码

javascript 复制代码
//
// 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方法。

  1. 如果处理的是异常信息,可以使用catch

演示代码:

javascript 复制代码
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上

演示代码

javascript 复制代码
 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格式的数据

演示代码

javascript 复制代码
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关键字,表示该函数为一个异步任务,不会阻塞后面函数的执行。

javascript 复制代码
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对象

javascript 复制代码
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会阻塞后面的代码

测试代码:

javascript 复制代码
// 两秒后获得一个随机数
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 案例演示

javascript 复制代码
<%@ 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>
相关推荐
cwj&xyp6 分钟前
Python(二)str、list、tuple、dict、set
前端·python·算法
dlnu20152506228 分钟前
ssr实现方案
前端·javascript·ssr
古木201912 分钟前
前端面试宝典
前端·面试·职场和发展
轻口味2 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王2 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发2 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀3 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪3 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef5 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端