资讯专栏INFORMATION COLUMN

将"回调地狱"按在地上摩擦的Promise

Yu_Huang / 2103人阅读

摘要:回调地狱的问题在于写法过于繁琐不够优雅代码维护炒鸡蛋疼,所以一直被前端程序猿所诟病,尤其是维护类似代码的时候简直日了一群哈士奇。,对象状态以和为分水岭。方法返回一个带有拒绝原因参数的对象摘自对的解释。并且返回的也是一个对象。

这是一段旁白

“异步虐我千百遍,我待异步如初恋”!!
做前端的同学做异步肯定都不陌生。因为JavaScript是单线程语言(也就是说不支持多线程编程,这不是废话么啊喂!),所以在JavaScript中处理异步问题也是经过了几代人的踩坑和开荒才有了今天的“花里胡哨”的解决方案。

回调(CallBack)

利用回调来实现异步是一直以来非常有效的解决方案,而且经久不衰。其背后的原理很简单,就是利用JavaScript中可以将函数作为参数传入另一个函数(万物皆对象)。举个栗子:

function callBack() {
    console.log("回调啦回调啦!!!");
}

function main(cb) {
    console.log("我会运行很久!")
    cb();
}

main(callBack);

下面一段代码中实现两个函数 callBackmain。随后将 callBack 传入到 main 函数中,当 main 函数执行到一个阶段时候会调用传入的回调函数 ( 此处是当main函数运行到底部时候就调用了回调函数 )。运行结果不言而喻:

这样的写法看起来貌似还行,写法简单明了,一看就懂。但是这里笔者要吐槽下去年自己的智商,且听慢慢道来:
去年在重构项目的时候,有一个页面需要展示 4 个下拉框而且下拉框的数据需要从后台拉取。所以笔者在ComponentWillMount(React项目)方法中执行了拉取数据的动作而且是分开独立拉取,类似于:

......

ComponentWillMount() {
    let data = {};
    fetchSelect1();
    fetchSelect2();
    fetchSelect3();
    fetchSelect4();
}

......

最后在四个方法中将数据存储到 data 对象中以供渲染选择框,但是后面出现了一个意想不到问题:总会有一个下拉框数据拉取失败。所以不得已采用了回调方式来处理,这里再狠狠得吐槽一下自己,如果那时候会用Promise,也不会那么尴尬。下面贴一下当时的代码:

/* fetch data source prop selects */
router.get("/fetch-selects", function(req, resp, next) {
    let path1 = config.BACKEND_API.BASE_URL + "/datasource/frequency";
    var reponseData = {};
    httpAgent.httpRequest({}, "JSON", config.BACKEND_API.TYPE, config.BACKEND_API.HOST, config.BACKEND_API.PORT, path1, "GET", function(data) {
        reponseData.frequency = data;
        let path2 = config.BACKEND_API.BASE_URL + "/datasource/category";
        httpAgent.httpRequest({}, "JSON", config.BACKEND_API.TYPE, config.BACKEND_API.HOST, config.BACKEND_API.PORT, path2, "GET", function(data) {
            reponseData.category = data;
            let path3 = config.BACKEND_API.BASE_URL + "/datasource/type";
            httpAgent.httpRequest({}, "JSON", config.BACKEND_API.TYPE, config.BACKEND_API.HOST, config.BACKEND_API.PORT, path3, "GET", function(data) {
                reponseData.type = data;
                let path4 = config.BACKEND_API.BASE_URL + "/datasource/process/type";
                httpAgent.httpRequest({}, "JSON", config.BACKEND_API.TYPE, config.BACKEND_API.HOST, config.BACKEND_API.PORT, path4, "GET", function(data) {
                    reponseData.process = data;
                    resp.json(reponseData);
                }, function(code, body) {

                })
            }, function(code, body) {

            })
        }, function(code, body) {

        })
    }, function(code, body) {

    })
});

当时用的Node项目做的中间层,这是一个路由。可以看出来其实就是在拉取完第一条数据后再调用另一个函数来拉取第二条数据,如此嵌套下去。好在只需要拉取 4 条数据,那如果有10条乃至100条数据需要拉取怎么办?那岂不是需要嵌套出一个很深很深的代码结构么?这就是臭名昭著的“回调地狱”。“回调地狱”的问题在于写法过于繁琐不够优雅、代码维护炒鸡蛋疼,所以一直被前端程序猿所诟病,尤其是维护类似代码的时候简直日了一群哈士奇。不仅仅是想死的心了,完全想删库走人啊喂!

Promise

当前端异步工作处于水深火热中时,一个英雄踏着七彩祥云而来,他,就是 Promise。让我们相信:一个承诺,终究会被兑现。

Promise的由来
Promise 先由社区提出来的概念,主要是用于解决前端的异步问题,庆幸的是它在ES6中也得到了实现。
什么是Promise

Promise 是一个状态机。这么说可能有点不好懂,上个代码先:

new Promise(function(resolve, reject) {
    try {
        resolve("Success")
    } catch (e) {
        reject(e);
    }
})

从上面可以看出几个重要的点:
1,Promise是一个构造函数
2,新建Promise对象需要传入执行器函数 (executor function)。
3,执行器函数中有两个参数 resolvereject。这两个也是执行器函数。

对此来解释下什么叫状态机
Promise对象有三个状态:pending, fulfilled, rejected,没图说个JB?

从图中可以看出,Promise对象的初始状态是pending ,如果经过了 resolve 方法,状态置为 fulfilled ;如果经过了 reject 方法,状态置为 rejected 。而且有三点需要明确:
1,Promise对象的状态转换只有 pending--->fulfilled 或者 pending--->rejected。没有其它形式的转换。
2,Promise 对象的状态一经转换则永久冻结,意思就是说比如状态被置为 fulfilled 后,无法再回到 pending。
3,Promise对象状态以resolvereject为分水岭。调用这个两个方法之前,都处于pending状态。

Promise.resolve()
Promise.resolve(value)方法返回一个以给定值 value 解析后的 Promise 对象

摘自MDN对 Promise.resolve() 的解释。简单的理解就是它用来返回任务执行成功后的返回值。Promise对象调用完这个方法后状态就被置为 fulfilled。

Promise.reject()
Promise.reject(reason)方法返回一个带有拒绝原因reason参数的 Promise 对象

摘自MDN对 Promise.reject() 的解释。Promise对象调用完这个方法后状态就被置为 rejected。

Promise.prototype.then()

看到这里可能会有这么一个问题:既然Promise用 resolve 和reject 返回处理结果,那如何获取到这个结果呢?那么then()就大有可为了。从小标题可以看出 then 方法被放在Promise的原型上,也就是说任何一个Promise对象都可以调用这个方法,不管何时何地。then()方法的参数为两个个执行器函数,第一个函数用来处理 resolve() 返回值,第二个函数用来处理 reject() 返回值。并且then()返回的也是一个 Promise 对象。举个

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96783.html

相关文章

  • 用原生js写一个"多动症"简历

    摘要:用原生写一个多动症的简历预览地址源码地址最近在知乎上看到方应杭用写了一个会动的简历,觉得挺好玩的,研究一下其实现思路,决定试试用原生来实现。 用原生js写一个多动症的简历 预览地址源码地址 最近在知乎上看到@方应杭用vue写了一个会动的简历,觉得挺好玩的,研究一下其实现思路,决定试试用原生js来实现。 showImg(https://segmentfault.com/img/remot...

    Y3G 评论0 收藏0
  • 展示JavaScript中异步与回调基本概念及回调地狱

      JavaScript异步与回调  一、前言  首先我们要记住的是异步和并行有着本质的区别。  并行,简单来说是一般指并行计算,就是说同一时刻有多条指令同时被执行,这些指令可能执行于同一CPU的多核上,或者多个CPU上,或者多个物理主机甚至多个网络中。  同步,一般指按照预定的顺序依次执行任务,只有当上一个任务完成后,才开始执行下一个任务。  异步,与同步相对应,异步指的是让CPU暂时搁置当前任...

    3403771864 评论0 收藏0
  • TypeScript 、React、 Redux和Ant-Design最佳实践

    摘要:使用官方的的另外一种版本和一起使用自动配置了一个项目支持。需要的依赖都在文件中。带静态类型检验,现在的第三方包基本上源码都是,方便查看调试。大型项目首选和结合,代码调试维护起来极其方便。 showImg(https://segmentfault.com/img/bVbrTKz?w=1400&h=930); 阿特伍德定律,指的是any application that can be wr...

    wangbinke 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<