当前位置 博文首页 > 前端开发博客:JavaScript 中 Promise对象 的部分使用特点

    前端开发博客:JavaScript 中 Promise对象 的部分使用特点

    作者:[db:作者] 时间:2021-07-03 22:02

    关注公众号?前端开发博客,回复“加群”

    加入我们一起学习,天天进步

    回调函数和异步编程是JavaScript的特点之一,但是JavaScript中传统的回调函数编写太麻烦,如果嵌套的回调函数层级过多,容易产生“回调地狱”的现象,代码变得非常难看。因此Promise在ES6被引入用来解决这个问题。

    Promise的基本方法

    const?promise1?=?new?Promise((resolve,?reject)?=>?{
    ????//?some?thing
    ????if(/*?some?*/)
    ????resolve(/*?some?*/)
    ????else
    ????reject(/*?some?*/)
    });
    
    promise1.then(v?=>?{
    ????//?resolve函数
    ????//?do?some
    },?e?=>?{
    ????//?reject函数
    ????//?do?some
    })
    

    代码中上面是创建Promise的过程。

    创建Promise时接受一个函数作为参数,这个函数就是作为Promise异步执行的主要函数。

    (resolve,?reject)?=>?{
    ????//?some?thing
    ????if(/*?some?*/)
    ????resolve(/*?some?*/)
    ????else
    ????reject(/*?some?*/)
    }
    

    执行结束后,执行resolve(成功)或者reject(失败)这两个函数。

    这两个函数就定义在.then的参数中。

    如果Promise没有执行resolve(成功)或者reject(失败),那么.then中的内容永远不会执行,这样就实现了异步操作。

    promise函数的异步特点

    Promise一个很有意思的特点,就是Promise函数中resolve(成功)或者reject(失败)是异步的:

    const?promise1?=?new?Promise((resolve,?reject)?=>?{
    ????reject('fail');
    ????console.log("promise1");
    });
    
    promise1.then(v?=>?console.log("then1"),?e?=>?console.log(e))
    

    浏览器执行结果:

    我们先调用reject,然后此时函数应该跳转到then中的reject函数执行。但是并没有。

    程序先输出了promise1然后再是fail。这说明resolve和reject是异步的,执行到resolve或reject时标记状态,然后继续执行完promise函数再进入then中。

    throw类似reject,但有区别

    throw通常情况下可以看作reject。

    const?promise1?=?new?Promise((resolve,?reject)?=>?{
    ????console.log("promise1");
    ????reject('fail');
    });
    
    const?promise2?=?new?Promise((resolve,?reject)?=>?{
    ????console.log("promise2");
    ????throw?'fail';
    });
    
    promise2.then(v?=>?console.log("then1"),?e?=>?console.log(e))
    

    上面的代码中,promise1和promise2的唯一区别是reject换成了throw,但效果是相同的,reject函数成功捕获了错误,把它当成是一个reject来处理。

    但throw的一个很大区别是,在promise函数中会同步执行,不会等待promise函数执行完毕再进入then。

    const?promise1?=?new?Promise((resolve,?reject)?=>?{
    ????reject('fail');
    ????console.log("promise1");
    });
    
    const?promise2?=?new?Promise((resolve,?reject)?=>?{
    ????throw?'fail';
    ????console.log("promise2");
    });
    
    promise2.then(v?=>?console.log("then1"),?e?=>?console.log(e))
    

    我们把reject和throw上调了,先抛出错误再输出。

    这时候promise函数中的console.log就没有机会输出了。

    这是由于考虑到reject和throw的语义是不一样的:

    throw表示函数执行出现了错误,不应该继续执行。

    reject表示执行失败,但是不表示有错误。

    then的链式执行

    const?promise1?=?new?Promise((resolve,?reject)?=>?{
    ????console.log("promise1");
    ????resolve("ok1");
    });
    
    const?promise2?=?new?Promise((resolve,?reject)?=>?{
    ????resolve('ok2');
    ????console.log("promise2");
    })
    
    promise1.then(v?=>?{
    ????console.log("then1")
    ????return?promise2;
    }).then(v?=>?console.log("then2",?v),?e?=>?console.log("then2",?e));
    

    在then中返回另一个Promise,或者抛出错误(throw),后面的then就会接受另一个Promise的状态,调用resolve或者reject。

    此时还可以看到,在创建Promise后,promise函数接已经在执行了,我们在then中返回的promise2时早就执行完毕,仅仅是把resolve或者reject状态传递给了下一个then。

    then中即使不返回另一个Promise,也可以链式执行:

    Promise.resolve(2).then(()?=>?console.log(1)).then(()?=>?console.log(2))
    

    这是因为resolve和reject函数默认返回一个promise。我们看到浏览器每次执行完毕后输出的最后结果就是这个promise。

    (看上面浏览器输出的最后一行)

    注意这个promise并不代表我们一开始建立的promise。

    如果我们指定了返回值,那么相当于返回默认的promise时附带了参数:

    reject可被省略

    then中的reject函数可以省略:

    const?promise1?=?new?Promise((resolve,?reject)?=>?{
    ????resolve('ok');
    ????console.log("promise1");
    });
    
    promise1.then(v?=>?console.log("then1"))
    

    但是如果此时调用reject也无法被then读取。

    catch可替代reject

    promise1.then(v?=>?console.log("then1"),?e?=>?console.log(e));
    
    promise1.then(v?=>?console.log("then1")).catch(e?=>?console.log(e))
    

    这两种形式是相等的。

    reject和catch的链式捕获

    在链式then中,rejcect和catch可以捕获之前任一层出现的错误。

    const?promise1?=?new?Promise((resolve,?reject)?=>?{
    ????throw?new?Error('fail');
    ????console.log("promise1");
    });
    
    const?promise2?=?new?Promise((resolve,?reject)?=>?{
    ????resolve('ok');
    ????console.log("promise2");
    })
    
    promise1.then(v?=>?{
    ????console.log("then1")
    ????return?promise2;
    }).then(v?=>?console.log("then2",?v),?e?=>?console.log("then2",?e));
    

    浏览器执行结果:

    从结果中我们看到,在链式then中,如果出现了reject或者throw,后面所有then中的resolve都不会被执行。上面then1就没被执行,且promise2虽然是resolve状态,但是依然没有被后面then2中的resolve接受,反而执行了then2的reject。

    改成catch的写法:

    promise1.then(v?=>?{
    ????console.log("then1")
    ????return?promise2;
    }).then(v?=>?console.log("then2",?v)).catch(e?=>?console.log("then2",?e));
    

    在执行完reject之后,promise就变为了resolve状态。如果后面还有链式then,就可以执行reslove了。

    可以看到,在执行完reject之后,后面then中的resolve也被执行了。

    最后,文章中描述的resolve状态,实际上应该是fulfilled(已完成)状态,为了方便理解,我就直接使用了resolve来描述。

    更多资料

    在这里介绍的仅仅是Promise中很少的性质,如果想了解Promise的更多用法,可以看看下面的资料:

    阮一峰 《ES6标准入门》Promise部分

    es6.ruanyifeng.com/#docs/promi…

    MDN文档 Promise部分

    developer.mozilla.org/zh-CN/docs/…

    文章为完全原创,转载请标注来源。

    原文:https://juejin.cn/post/6933470987576934414

    作者:jzplp

    - EOF -

    相关文章??点击标题可跳转

    15道ES6 Promise实战练习题

    手写Promise核心原理,再也不怕面试官问我Promise原理

    精读JS系列(9b) Promise — 回调地狱、Promise构造器

    觉得本文对你有帮助?请分享给更多人

    推荐关注「前端开发博客」,提升前端技能

    后台回复以下关键字:

    1. 回复「1024」领取前端进阶资料

    2. 回复「电子书」领取海量面试和JS资料

    3. 回复「资料」领取前端群分享及培训机构的资料

    4. 回复「Vue」获取 Vue 精选文章

    5. 回复「面试」获取 面试 精选文章

    6. 回复「JS」获取 JavaScript 精选文

    7. 回复「CSS」获取 CSS 精选文章

    如果觉得这篇文章还不错,来个【分享、点赞、在看】三连吧,让更多的人也看到~

    最后,大家点个在看,星标我的公众号,支持我继续更新好文章,谢谢。

    cs
    下一篇:没有了