• 后端程序员入门react笔记(六)- 讲透Promise与Fetch


    js声明变量的三种方式

    我们通常使用 var let 和const来声明js的变量,但是这三者有什么区别呢?

    • var的变量可以是全局的或者函数作用域,而let和const只能是块级作用域
    • var和let可以重新赋值,而const是不允许重新赋值的,
    • var可以在一个作用域内重新声明,let和const只能声明一次
    • 一般来说,建议使用 let 和 const 来声明变量,因为它们提供了更严格的作用域控制和可变性。var 仍然可以在某些情况下使用,例如在需要全局作用域的变量时。

    js的异步编程

    用过ajax请求的人应该都知道有这么一个参数async,即是否异步,如果是异步,则设置为true,如果是非异步,则为false,我们举例看一下

    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script>
        console.log(1);
        $.ajax({
            url: "./a.json",
            type: "post",
            dataType: "text",
            async: true,//异步
            data: {},
            success: function(data){
                console.log(2);
            }
        });
        console.log(3);
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 结果返回的是1 3 2 ,即js并没有等待ajax的结果,而是继续往下执行代码,Ajax的返回结果是通过success回调函数调用的。如果把async设置为false,则结果是1 2 3
      在这里插入图片描述

    回调地狱

    那么这个时候来了一个需求,要求根据第一个Ajax的请求结果作为条件来发起第二个Ajax请求,然后以第二个Ajax的请求结果作为条件发送第三个Ajax请求,那么形式可能就是这样的

    <script>
        $.ajax({
            url: "./a.json",
            type: "post",
            dataType: "text",
            async: true,
            success: function(data){
                $.ajax({
                    url: "./a.json",
                    type: "post",
                    dataType: "text",
                    async: true,
                    success: function(data){
                        $.ajax({
                            url: "./a.json",
                            type: "post",
                            dataType: "text",
                            async: true,
                            success: function(data){
                            }
                        })
                    }
                })
            }
        })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    一层层的嵌套,这就是回调地狱。这即不好看,也不好写,逻辑更是混乱。那么怎么办呢?至少作为后端,我们取使用orm取数据的时候都会用到一种链式操作,比如Model(user).Where("email = ?",query.Email).Where("password = ?",utils.Md5Encode(query.PassWord)).First(user)。我们永远可以在之前的操作结果上加条件。那么js有没有这种写法呢?有的,这个东西就叫做promise

    promise

    Promise是ES6新增的一个类,可以翻译为承诺(许诺、契约等),promise允许你将异步操作链接在一起,并以同步的方式处理它们的结果。
    既然是一个类,就会有构造函数,promise的构造函数叫executor,我们可以这样创建一个promise

    const myPromise = new Promise((resolve, reject) => {
      // 这里是一些异步操作
      if (success) {
        resolve(result);
      } else {
        reject(error);
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    处理promise

    有两种处理promise的方法

    • .then():当 Promise 成功完成时调用。它接受一个回调函数,该函数将接收 Promise 的结果作为参数。
    • .catch():当 Promise 失败时调用。它接受一个回调函数,该函数将接收 Promise 的错误作为参数。注意,如果.then里面处理的err则catch无法再次捕捉
      注意,.then() 和 .catch() 回调总是返回一个新的 Promise,这使得可以链接 Promise。
    var promise = new Promise(function(resolve, reject) {
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    promise.then(function(value) {
      // 如果调用了resolve方法,执行此函数
    }, function(value) {
      // 如果调用了reject方法,执行此函数
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    演示

    <script>
        var getJSON = function (url) {
    
            //创建一个promise并返回
            return new Promise(function (resolve, reject) {
                //进行一些异步操作
                const client = new XMLHttpRequest();
                client.open("GET", url);
                client.onreadystatechange = handler;
                client.responseType = "json";
                client.setRequestHeader("Accept", "application/json");
                client.send();
                function handler() {
                    if (this.readyState === 4) {
                        if (this.status === 200) {
                            //如果成功则执行resolve
                            resolve(this.response);
                        } else {//如果失败则执行resolve
                            reject(new Error(this.statusText));
                        }
                    }
                };
    
            });
        };
        getJSON("./a.json").then(function(json) {
            console.log(json);
        }, function(error) {
            console.error('出错了', error);
        });
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    链式调用

    Promise 可以链接在一起,形成一个链式调用:

    myPromise1
      .then((result1) => {
        return myPromise2(result1);
      })
      .then((result2) => {
        // 处理最终结果
      })
      .catch((error) => {
        // 处理错误
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    promise的状态

    promise实例的状态有三种

    • pending: 初始状态,未执行
    • fulfilled: 操作完成
    • rejected: 操作失败

    调用流程图

    在这里插入图片描述

    一道面试题

    下面这道题目,输出结果是多少?

        console.log(1);
        new Promise(function (resolve, reject){
            console.log(2);
            reject(true);
            console.log(3);
            window.setTimeout(function (){
                console.log(4);
                resolve(false);
            }, 0);
        }).then(function(){
            console.log(5);
        }, function(){
            console.log(6);
        });
        console.log(7);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 接下来我们解析一下上面的代码,console.log(1)执行输出1,构造promise对象要先执行executor,输出console.log(2);异步调用reject(true);继续输出console.log(3);setTimeout也是异步调用,被放到事件队列。promise对象创建完毕,继续console.log(7);reject事件发起回调,执行console.log(6);setTimeout发起回调,执行 console.log(4);,由于reject之后 promise的状态无法再次被修改,resolve(false)不会改变状态,所以不执行,最终执行结果就是 1 2 3 7 6 4

    fetch

    如果你了解xhr,那么你就很容易明白fetch,因为fetch就是js原生支持的,xhr的替代方案。Fetch被称为下一代 Ajax 技术,采用 Promise 的方式来处理数据。它是一种简洁明了的API,比 XMLHttpRequest 更加简单易用。

    fetch的初试

    我们来简单实现一个fetch的demo,我们前面说了,是js原生的api,无需引入其他包

        let url="./a.json"
        fetch(url).then((res)=>{
            console.log(res);
        }).catch((err)=>{
            console.log(err);
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 我们查看输出,请求的返回值是一个json,但是这个fetch返回的response没有我们的数据啊,为什么?这我们就要说到了关注分离原则
      在这里插入图片描述

    关注分离

    关注分离是一种软件设计原则,旨在将不同的功能模块分离开来,使其各自专注于特定的任务。在 Web 开发中,这意味着将输入、输出和状态跟踪等不同方面的功能分离开来,以便更好地组织代码并提高可维护性。
    比如咱们上面举例的这个fetch,response并不是说我获取到了数据,重点在于我链接服务成功了,服务器给我响应了,不管是404还是500还是200,只要服务器响应我了,我就认为我任务完成了。
    在这里插入图片描述
    那么我们怎么获取数据呢?那我们就要在这个基础上,继续获取promise里面的内容

        let url="./a.json"
        fetch(url)
            .then((res)=>{
            console.log(res);
            //把结果作为promise返回
            return  res.json()
        })
            .then((json)=>{
                //处理上一个返回的promise
            console.log(json);
        })
            .catch((err)=>{
            console.log(err);
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    一个完整的fetch请求

    一个完整的fetch,在url后面,其实还可以跟一个config对象,config对象属性如下,可以配置请求方式,data,headers等信息

    {
        /** A BodyInit object or null to set request's body. */
        body?: BodyInit | null;
        /** A string indicating how the request will interact with the browser's cache to set request's cache. */
        cache?: RequestCache;
        /** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */
        credentials?: RequestCredentials;
        /** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */
        headers?: HeadersInit;
        /** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */
        integrity?: string;
        /** A boolean to set request's keepalive. */
        keepalive?: boolean;
        /** A string to set request's method. */
        method?: string;
        /** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */
        mode?: RequestMode;
        /** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */
        redirect?: RequestRedirect;
        /** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */
        referrer?: string;
        /** A referrer policy to set request's referrerPolicy. */
        referrerPolicy?: ReferrerPolicy;
        /** An AbortSignal to set request's signal. */
        signal?: AbortSignal | null;
        /** Can only be null. Used to disassociate request from any Window. */
        window?: null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    async 和await

    上面例子中,我们通过.then返回的promise再次.then ,这明显是有些繁琐的。那么怎么办?我们这里就可以用到async 和await,

        let url = "./a.json"
        async function getJson() {
            try {
                let res = await fetch(url);
                if (res.status !== 200) {
                    throw new Error("请求失败")
                }
                const json = await res.json();
                console.log(json);
            } catch (e) {
                console.log(e);
            }
        }
        getJson()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    那么这里面的async和await是什么意思呢?

    • async 关键字提供了一种更简单的方法来处理基于异步 Promise 的代码。在一个函数的开头添加 async,就可以使其成为一个异步函数。注意,async 函数总是返回一个 Promise 对象。
    • 在异步函数中,我们可以在调用一个返回 Promise 的函数之前使用 await 关键字。这使得代码在该点上等待,直到 Promise 被完成,这时 Promise 的响应被当作返回值,或者被拒绝的响应被作为错误抛出。这样我们就能够编写像同步代码一样的异步函数,注意,只能在 async 函数内使用 await 关键字,用于等待 Promise 对象 resolved 后返回结果。
    • await 会暂停 async 函数的执行,等待 Promise 返回结果,然后恢复函数执行并返回数据。
    • await 表达式如果抛出错误,会被捕获并返回 Promise 对象的 reject 状态。

    promise的并行调用和链式调用示例

    <script>
        let url1 = "./a1.json"
        let url2 = "./a2.json"
        let url3 = "./a3.json"
    
        async function getJson1(n) {
            console.log(n);
            try {
                let res = await fetch(url1);
                if (res.status !== 200) {
                    throw new Error("请求失败")
                }
                return res.json();
            } catch (e) {
                console.log(e);
            }
        }
        async function getJson2(n) {
            console.log(n);
            try {
                let res = await fetch(url2);
                if (res.status !== 200) {
                    throw new Error("请求失败")
                }
                return res.json();
            } catch (e) {
                console.log(e);
            }
        }
        async function getJson3(n) {
            console.log(n);
            try {
                let res = await fetch(url3);
                if (res.status !== 200) {
                    throw new Error("请求失败")
                }
                return res.json();
            } catch (e) {
                console.log(e);
            }
        }
    
        //并行执行多个异步操作
        async function getAll(){
            //等待所有执行完毕
            let res= await Promise.all([getJson1(1), getJson2(2), getJson3(3)])
            let total=res[0].age+res[1].age+res[2].age
            console.log(total);
            return total;
        }
        getAll()
        //链式调用 根据前面的结果调用下一个
        async function chain(){
            let init=0
            let res1=await getJson1(init);
            let res2=await getJson2(res1.age);
            let res3=await getJson3(res2.age);
            console.log(res3.age);
        }
        chain()
    
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
  • 相关阅读:
    洛谷P1706 全排列问题
    在ITSM中,实施变更管理的重要因素!
    Synchronized和volatile 面试简单汇总
    【个人首测】百度文心一言 VS ChatGPT GPT-4
    新国标红绿灯识别的程序逻辑,一看就会
    Windows上的实用CMD命令
    【Android】Android 项目里面为啥有两个地方设置Gradle
    整合mysql多个bool值字段,用&查询
    传统企业数字化转型,处理好业务问题是关键
    基于卡尔曼滤波进行四旋翼动力学建模(Simulink&Matlab)
  • 原文地址:https://blog.csdn.net/qq_39962403/article/details/136261758