ES6的Promise对象

Promise对象,ES6新增的一个全新特性,今天我们要好好学习一下它。
Promise的设计初衷

首先,我们先一起了解一下,为什么要设计出这么一个玩意儿,用它是为了解决什么问题?

    带着这个问题,我们来回想一下日常开发中,经常需要用到ajax请求数据,拿到数据后,再进行一些处理。

    可有一次,你需要用ajax进行多次请求,而且,每次请求都依赖上一次请求返回的数据来作为参数,然后继续发出请求,你把代码写成了这样:
//------请求A 开始---------
    $.ajax({
        success:function(res1){


            //------请求B 开始----
            $.ajax({
                success:function(res2){


                    //----请求C 开始---
                    $.ajax({
                        success:function(res3){
                        }
                    });
                    //---请求C 结束---


                }    
            });
            //------请求B 结束-----


        }
    });
    //------请求A 结束---------
    上面的案例,假设请求C需要依赖请求B返回的数据,所以,C只能放在B的success函数内;B需要依赖A请求得到的数据作为参数,所以,B只能放在A的success函数内;也就是:请求A包含着请求B,请求B又包含了请求C。

    就这样,请求顺序为:请求A -> 请求B -> 请求C,最后你也能顺利的完成了任务。

传统写法的不足
但是这样写,会有一些缺点:

  1. 如果存在多个请求操作层层依赖的话,那么以上的嵌套就有可能不止三层那么少了,加上每一层还会有复杂的业务逻辑处理,代码可读性也越来越差,不直观,调试起来也不方便。如果多人开发的时候没有足够的沟通协商,大家的代码风格不一致的话,更是雪上加霜,给后面的维护带来极大的不便。

  2. 如果请求C的需要依赖A和B的结果,但是请求A和B缺互相独立,没有依赖关系,以上的实现方式,就使得B必须得到A请求完成后才可以执行,无疑是消耗了更多的等待时间。

    既然使用这种回调函数层层嵌套(又称:回调地狱)的形式存在缺点,ES6想了办法治它,所以就有了Promise的出现了。

    那么我们就知道了:Promise对象能使我们更合理、更规范地进行处理异步操作。

Promise的基本用法
接下来,我们就看看它的基本用法:

    let pro = new Promise(function(resolve,reject){
        //....
    });
Promise对象是全局对象,你也可以理解为一个类,创建Promise实例的时候,要有那个new关键字。参数是一个匿名函数,其中有两个参数:resolve和reject,两个函数均为方法。resolve方法用于处理异步操作成功后业务;reject方法用于操作异步操作失败后的业务。

Promise的是三种状态
Promise对象有三种状态:
pending:刚刚创建一个Promise实例的时候,表示初始状态;
fulfilled:resolve方法调用的时候,表示操作成功;
rejected:reject方法调用的时候,表示操作失败;

    状态只能从 初始化 -> 成功  或者  初始化 -> 失败,不能逆向转换,也不能在成功fulfilled 和失败rejected之间转换。
    let pro = new Promise(function(resolve,reject){
        //实例化后状态:pending

        if('操作成功'){
            resolve();
            //resolve方法调用,状态为:fulfilled
        }else{
            reject();
            //reject方法调用,状态为:rejected
        }
    });
    

上面的注释,讲清楚了一个Promise实例的状态改变情况。

    初始化实例后,对象的状态就变成了pending;当resolve方法被调用的时候,状态就变成了:成功fulfilled;当reject方法被调用的时候,状态就会有pending变成失败rejected。

then( )方法
了解了Promise的创建和状态,我们来学习一个最重要的实例方法:then( )方法。

    then( )方法:用于绑定处理操作后的处理程序。
    pro.then(function (res) {
        //操作成功的处理程序
    },function (error) {
        //操作失败的处理程序
    });
    

参数是两个函数,第一个用于处理操作成功后的业务,第二个用于处理操作异常后的业务。

catch( )方法

对于操作异常的程序,Promise专门提供了一个实例方法来处理:catch( )方法。

   pro.catch(function (error) {
        //操作失败的处理程序
    });
    

catch只接受一个参数,用于处理操作异常后的业务。

    综合上面的两个方法,大家都建议将then方法用于处理操作成功,catch方法用于处理操作异常,也就是:
   pro.then(function (res) {
        //操作成功的处理程序
    }).catch(function (error) {
        //操作失败的处理程序
    });

之所以能够使用链式调用,是因为then方法和catch方法调用后,都会返回promise对象。

    讲了那么多,如果你之前一点都没接触过Promise的话,现在一定很懵逼,没关系,下面我们用一个案例来串联前面的知识点,演示一下,认真阅读注释:
 //用new关键字创建一个Promise实例
    let pro = new Promise(function(resolve,reject){
        //假设condition的值为true
        let condition = true;

        if(condition){
            //调用操作成功方法
            resolve('操作成功');
            //状态:pending->fulfilled
        }else{
            //调用操作异常方法
            reject('操作异常');
            //状态:pending->rejected
        }
    });

    //用then处理操作成功,catch处理操作异常
    pro.then(function (res) {

        //操作成功的处理程序
        console.log(res)

    }).catch(function (error) {

        //操作失败的处理程序
        console.log(error)

    });
    //控制台输出:操作成功

上面案例的注释十分详细,串联起了上面介绍的所有知识点:创建实例,状态转换,then方法和catch方法的使用。

    由于我们设置了变量condition的值为true,所以执行后控制台输出的结果是:“操作成功”。

    上面就是Promise用于处理操作异常的这个过程;但是,正如文章开头讲到的,如果多个操作之间层层依赖,我们用Promise又是怎么处理的呢?

完整案例
我们看看下面的案例,代码有点长,但是一点都不复杂:

let pro = new Promise(function(resolve,reject){

        if(true){
            //调用操作成功方法
            resolve('操作成功');
        }else{
            //调用操作异常方法
            reject('操作异常');
        }
    });

//用then处理操作成功,catch处理操作异常
    pro.then(requestA)
        .then(requestB)
        .then(requestC)
        .catch(requestError);

    function requestA(){
        console.log('请求A成功');
        return '请求B,下一个就是你了';
    }
    function requestB(res){
        console.log('上一步的结果:'+res);
        console.log('请求B成功');
        return '请求C,下一个就是你了';
    }
    function requestC(res){
        console.log('上一步的结果:'+res);
        console.log('请求C成功');
    }
    function requestError(){
        console.log('请求失败');
    }

    //打印结果:
    //请求A成功
    //上一步的结果:请求B,下一个就是你了
    //请求B成功
    //上一步的结果:请求C,下一个就是你了
    //请求C成功

案例中,先是创建一个实例,还声明了4个函数,其中三个是分别代表着请求A,请求B,请求C;有了then方法,三个请求操作再也不用层层嵌套了。我们使用then方法,按照调用顺序,很直观地完成了三个操作的绑定,并且,如果请求B依赖于请求A的结果,那么,可以在请求A的程序用使用return语句把需要的数据作为参数,传递给下一个请求,案例中我们就是使用return实现传递参数给下一步操作的。
更直观的图解
如果你还是是懂非懂,没关系,前端君拼了,上图:


TIM截图20180109092942.jpg

图有点粗糙,但是能反应出上面程序的执行过程,帮助大家加深理解。

    除了提供了实例方法以外,Promise还提供了一些类方法,也就是不用创建实例,也可以调用的方法。

    下面,我们来学习几个重要的。

Promise.all( )方法
Promise.all( )方法:接受一个数组作为参数,数组的元素是Promise实例对象,当参数中的实例对象的状态都为fulfilled时,Promise.all( )才会有返回。

    我们来演示一下:
//创建实例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('实例1操作成功');
        },5000);
    });
    
    //创建实例pro2
    let pro2 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('实例2操作成功');
        },1000);
    });

    
    Promise.all([pro1,pro2]).then(function(result){
        console.log(result);
    });
    //打印结果:["实例1操作成功", "实例2操作成功"]

上述案例,我们创建了两个Promise实例:pro1和pro2,我们注意两个setTimeout的第二个参数,分别是5000毫秒和1000毫秒,当我们调用Promise.all( )方法的时候,会延迟到5秒才控制台会输出结果。

    因为1000毫秒以后,实例pro2进入了成功fulfilled状态;此时,Promise.all( )还不会有所行动,因为实例pro1还没有进入成功fulfilled状态;等到了5000毫秒以后,实例pro1也进入了成功fulfilled状态,Promise.all( )才会进入then方法,然后在控制台输出:["实例1操作成功","实例2操作成功"]。

    这个方法有什么用呢?一般这样的场景:我们执行某个操作,这个操作需要得到需要多个接口请求回来的数据来支持,但是这些接口请求之前互不依赖,不需要层层嵌套。这种情况下就适合使用Promise.all( )方法,因为它会得到所有接口都请求成功了,才会进行操作。

Promise.race( )方法
另一个类似的方法是Promise.race()方法:它的参数要求跟Promise.all( )方法一样,不同的是,它参数中的promise实例,只要有一个状态发生变化(不管是成功fulfilled还是异常rejected),它就会有返回,其他实例中再发生变化,它也不管了。

//初始化实例pro1
    let pro1 = new Promise(function(resolve){
        setTimeout(function () {
            resolve('实例1操作成功');
        },4000);
    });

    //初始化实例pro2
    let pro2 = new Promise(function(resolve,reject){
        setTimeout(function () {
            reject('实例2操作失败');
        },2000);
    });

    Promise.race([pro2,pro1]).then(function(result){
        console.log(result);
    }).catch(function(error){
        console.log(error);
    });
    //打印结果:实例2操作失败

同样是两个实例,实例pro1不变,不同的是实例pro2,这次我们调用的是失败函数reject。

    由于pro2实例中2000毫秒之后就执行reject方法,早于实例pro1的4000毫秒,所以最后输出的是:实例2操作失败。

    以上就是对Promise对象的内容讲解,上面提到了一个概念:回调地狱;指的是过多地使用回调函数嵌套,使得调试和维护起来极其的不便。

总结:Promise是一个让开发者更合理、更规范地用于处理异步操作的对象,它有三种状态:初始化、操作成功、操作异常。使用实例方法:then( ) 和 catch( ) 来绑定处理程序;还提供了类方法:Promise.all( ) 和 Promise.race( )。
摘抄自前端君教程-->
http://mp.weixin.qq.com/s/jUdSRsF6fBik1ZyVSLAk-g

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容