• JavaScript(四):遇见Promise


    1、概述

    大家好,我是欧阳方超。
    本次我们聚焦一下JavaScript中的Promise。

    2、Promise介绍

    2.1、Promise概念

    在引入Promise前,我们先假设这样一个场景,需要请求网络才能获得一些数据然后将获得的数据渲染到页面当中,你可能已经想到这不就是基本的AJAX操作吗,没错,但是你是否还记得AJAX操作在处理正常结果和异常结果时代码写得是多么的繁琐,不够清爽,如下对成功和失败方法的调用都写进同一个函数了。

    request.onreadystatechange = function () {
        if (request.readyState === 4) {
            if (request.status === 200) {
                return success(request.responseText);
            } else {
                return fail(request.status);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    而Promise就是一种的以更清爽的方式解决这类问题的技术,接着上面的假设场景,我们将“需要请求网络才能获得一些数据”的操作定义为生产者代码——产生数据的代码,将“然后将获得的数据渲染到页面当中”的操作定义为消费者代码——在生产者代码完成取数据的操作后立即使用其取得的数据,而Promise就是将生产者代码与消费者代码连接在一个的JavaScript对象,由此可以看出Promise有两个特点,其一是Promise是一个JavaScript对象,其二是它可以将生产者代码与消费者代码连接起来。
    现在已经不负责任地引入了Promise的概念,再看一下它的语法:

    let myPromise = new Promise(function (resolve, reject) {
            //这里写生产这代码
            
        });
    
    • 1
    • 2
    • 3
    • 4

    创建Promise对象时需要给它的构造器传一个函数,该函数被称为executor,通过构造器创建Promise对象时传入的函数会被立即执行,该函数还有两个参数(resolve、reject),它们是JavaScript自身提供的回调,当executor无论何时执行完后它应该调用以下两个回调函数中的一个:

    • resolve(value)——如果任务成功完成并返回了结果value;
    • reject(error)——如果出现了错误,error是产生的错误;
      因此概括来说,executor执行一项工作,执行结束后如果成功则调用resolve如果失败则调用reject。

    2.3、Promise的属性

    使用Promise构造器创建的Promise对象,具有以下内部属性:
    state——promise对象的最初状态为pending,在resolve()函数被调用之后,状态变为fulfilled,在reject()函数被调用之后状态变为rejected;
    result——promise对象的最初结果为undefined,在resolve(value)被调用后变为调用该函数时传入的值,在reject(err)被调用之后变成传入的err值;
    state与result的变化详情可见下面三幅图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    promise状态的变化可以总结到下图中:
    在这里插入图片描述
    executor会在执行一项任务(一般会比较耗时)后调用resolve或reject来改变promise对应的状态。与promise的最初状态pending不同的是,一个fulfilled或rejected的promise都会被称为“settled”。

    注意事项:

    1. executor只能调用一个resolve或一个reject。因为promise对象状态的修改是最终的,调用完一个resolve或一个reject后其状态已被修改。这么设计的目的是保证一个executor执行完后只有一个结果,要么是正确结果要么是错误。
    2. resolve/reject只需要一个参数(或不包含任何参数),额外传的参数将被忽略。
    3. 当executor中出了问题需要调用reject时,应该给其传Error对象(或集成自Error的对象)。
    4. executor通常是异步执行一些操作,并在一段时间后调用resolve/reject,但是这不是必须的,也可以立即调用resolve或reject:
    let promise = new Promise((resolve,reject)=>{
            resolve();
        });
    
    • 1
    • 2
    • 3
    1. Promise对象的state、result属性是内部的,无法直接访问,但是可以使用.then、.catch、.finally方法间接做到这一点。

    2.3、消费者:then、catch

    一个Promise对象的作用是把executor与消费者连接起来,消费者端负责接收结果或错误。可以使用.then、.catch方法注册消费者端的函数。

    2.3.1、then方法

    then方法是最重要的方法,其语法如下:

    promise.then(
            function (result) {
                //处理成功的结果
            },
            function (error) {
                //处理错误的结果
            }
        );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上面then的第一个参数是一个函数,该函数会在promise的状态变为fulfilled且接收到结果后执行;
    then的第二个参数也是函数,该函数会在promise的状态变为rejected之后执行。
    下面是一个then的第一个参数被执行的情况:

    let promise = new Promise((resolve,reject)=>{
            resolve(123);
        });
        //then的第一个参数将被执行
        promise.then(
            (res) => {
                console.log(res)
            },
            (error) => {
                //不执行
            }
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    下面是一个then的第二个参数被执行的情况:

    let promise = new Promise((resolve,reject)=>{
            reject(new Error("异常"));
        });
        //then的第二个参数将被执行
        promise.then(
            //不执行
            (res) => {
                console.log(res);
            },
            (error) => {
                console.log(error);
            }
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    其实小伙伴的代码都不是这样写的,大家都是只给then传一个参数,这样是可以的,如果只对成功的结果感兴趣,就可以只为then提供一个参数:

    let promise = new Promise((resolve,reject)=>{
            resolve("ok");
        });
        //为then只提供一个参数
        promise.then(
            (res) => {
                console.log(res);
            }
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.3.2、catch方法

    如果只对error感兴趣,其实可以对then的第一个参数传null,第二个参数传处理错误的函数:

    let promise = new Promise((resolve,reject)=>{
            reject(new Error("异常"));
        });
        //为then只提供一个参数
        promise.then(null,
            (err) => {
                console.log(err);
            }
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上面处理错误时还有一种写法,那就是使用catch——据观察这也是小伙伴们用的多的方法,给catch只传一个处理错误的函数即可:

    let promise = new Promise((resolve,reject)=>{
            reject(new Error("异常"));
        });
        //为then只提供一个参数
        promise.catch((err) => {
                console.log(err);
            }
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.3.3、finally方法

    finally是无论promise的状态如何都会执行的一个操作,这个似乎比较好理解。下面是使用它的注意事项:

    1. finally接收到回调函数是没有参数的,在finally中只只掉promise的的状态为setted,具体是fulfilled还是rejected是不清楚的。
    2. finally是会将结果或error传递给下一个合适的程度的,这意味着不一定必须在链式调用的最后调用finally方法,如下面的示例:
    let promise = new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve("ok");
            })
        });
        promise.finally(() => {
            console.log("this is finally");
        }).then((result) => {
                console.log(result);
            }
        )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. finally也不应该返回任何内容,如果返回了,其返回值将会被忽略。这里有一个例外,如果finally在执行回调函数时抛出error,则这个error会被传到最近的error处理程序。

    3、对settled状态的promise添加处理程序

    当promise的状态为pending时,.then、.catch、.finally都会等待其结果。我们可以为一个settled状态的promise添加处理程序,此时处理程序会立即执行。

    4、总结

    在通过网络请求取得数据的场景中,生产者代码产生数据,消费者使用数据,而Promise就是将生产者代码与消费者代码连接在一个的JavaScript对象。
    我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。

  • 相关阅读:
    Unity初学者肯定能用得上的50个小技巧
    设计模式 - 概览
    mysql5.7.38安装教程
    ffmpeg强制关键帧间隔(key frame, gop size, gop duration)
    Mirror 镜像站点的使用
    【java苍穹外卖项目实战三】nginx反向代理和负载均衡
    基于Springboot+vue的停车场管理系统(Java毕业设计)
    写一下关于部署项目到服务器的心得(以及遇到的难处)
    Spring实战之有条件的加载bean/按某个条件加载bean
    2022.5.15-参加北京青少年程序设计展示活动海淀区赛(失误了,三等奖)
  • 原文地址:https://blog.csdn.net/u012288582/article/details/126644429