趣味探索promise的秘密

作者: 会飞小超人 | 来源:发表于2017-02-08 17:28 被阅读176次

    网上也有很多关于promise的讲解,但感觉都停留在理论层面,对于读者来说,看了一遍,似乎已经全部明白了,但真要用起来,发现还是无从下手。因此,本文旨在用人话为小伙伴们解释一下promise到底真正怎么用。
    最近的项目里包含了大量的表单交互,必然免不了各种异步请求,promise使用相当频繁。我想,是时候来与小伙伴们分享一下我遇到的各种promise的坑了。

    promise干嘛用的?----一般用于异步操作。
    为什么能用来异步操作?----因为能等一个函数执行完之后执行下一个函数操作。
    懂了吗?----懂了
    那好,用一个我看看----额,那个。。到底怎么写来着。。
    你不是都懂了吗?----是啊,道理我都懂啊,但为什么就是不会用呢。。

    哈哈,这就是我最初使用promise的深刻感受,明明那些概念都懂,语法也并不复杂,可就是实际用的时候用不出来。如果你也有这种感受,那么,恭喜你,来对地方了,跟着我继续探索promise的秘密吧!

    promise和then的配套使用

    我们来想象这么一个生活情境:

    开学了,幼儿园里的小朋友们排排坐,老师说:小朋友们,让我们来介绍自己的名字好不好呀,那么,从第一排的小红开始,依次往下介绍,好,我们开始吧!
    小红站起来说,大家好,我叫小红,
    接着小强站起来说,大家好,我叫小强


    接着,轮到小明了,小明有个缺点,就是一紧张就口吃,只听他红着脸,站起来说:大。。大。。大。。大。。家。。好。。好。。
    还没说完呢,后面的急性子小刚就站了起来,说:大家好,我叫小刚
    说完,小刚后面的同学就接着站了起来,继续介绍自己。
    最后,最后一排的同学也介绍完自己,坐下去了。此时,到家听到小明的声音:我叫。。叫。。叫。。。叫。。叫。。
    此时,老师也不耐烦了,说:行了,小明,坐下吧
    可怜的小明,就这样,连一个自我介绍都没有完成。。。

    好了,亲,记住上面的故事了吗?我们开始学习promise。本来大家都好好的,按顺序自我介绍,挺好。但毕竟人生不如意十之八九,谁能想到小明有些口吃呢,所以小刚就抢着自我介绍了,你可能想说,这都怪小刚,可人家小刚还委屈呢,老师说的,按照座位来的,既然小明已经开始自我介绍了,那我就开始嘛,谁知道他这么慢呢。
    对,小明就是这个异步操作,异步请求和接受服务器相应都是需要时间的,但是人家正常的js程序不管这些,一条语句执行了,下一条语句就会接着执行,不会去管上一条语句又没有完成,程序员你也没说要必须等上一条语句执行完才能执行下一条啊。
    这样的问题在哪里呢,问题在最后,老师统计班上同学姓名的时候,会直接把小明漏掉,因为统计的时候,他都没有自我介绍完啊!也就是说,你后面的语句需要用到前面异步的返回值的时候,就会接收不到!
    故事还没完呢,接下来听第二段:

    第二年,小明班上换了一个班主任,这回小明学聪明了,提前就跟老师说明了他的情况,于是老师想了个办法。
    老师说,同学们,我们今年的自我介绍,换一种方式,每一位同学在自我介绍之前,必须要先说出前一位同学的名字,才能进行自我介绍,这样小刚就算再性急,也必须要等小明自我介绍完了才能开始自我介绍。
    多么聪明的老师啊,这样,任何同学,都能做完一个完整的自我介绍了!

    老师的做法,就是promise。后一个同学必须要等钱一个同学说完,因为他需要前一个同学的名字。来看promise的语法:

    new Promise((resolve,reject)=>{
    // dosomething(异步操作);
        resolve(value)
    })
    

    把老师的做法翻译成js语句就是这样:

    var m=new Promise((resolve,reject)=>{
        console.log('小明开始自我介绍')
        setTimeout(()=>{//用定时器模拟的异步操作(小明口吃)
            var name='小明'
            resolve(name)
        },3000)
    })
    m.then(model=>{
        var preName=model
        var name='小刚'
        console.log(preName)
        console.log(name)
    })
    
    

    执行结果如下:

    QQ截图20170208152427.jpg

    你会发现,不光是小明在等待了3秒后才打印出来,小刚也一样是等待了3秒后才打印出来,这充分说明了promise的特点,同时我都不用再解释,相信then的用法相信小伙伴们也能很轻易的学会了!

    Promise.all的应用场合

    接下来,说说Promise.all的应用场合,同样,上故事:

    开学第二天,老师需要统计学生们的个人信息,于是让班长小红给每人发了一张个人信息表,让他们填写,并嘱咐她,一定要等所有的同学都填完了,再收上来。小红心里偷偷的笑了,肯定老师已经知道小华写字特别慢了。。

    天啊,这到底是怎样一个班级,口吃的、写字慢的。。。为老师默哀一秒钟。。。
    咳咳,说正事。其实老师的嘱咐,便是Promise.all的做法,Promise.all语法如下:

    Promise.all(arr)
    

    arr是一个包含Promise对象元素的数组,可能这么说还是有点抽象,那接下来把老师的做法翻译成js:

    var xiaoming=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaoming',age:'5'}
            resolve(info)
        },2000)
    })
    var xiaogang=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaogang',age:'6'}
            resolve(info)
        },1000)
    })
    var xiaohua=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaohua',age:'7'}
            resolve(info)
        },5000)
    })
    var arr=[xiaoming,xiaogang,xiaohua]
    Promise.all(arr).then(model=>{
        console.log(model)
    })
    

    结果如你所料,在等待了5秒后,控制台打印出了三个人的信息:

    QQ截图20170208154348.png

    Promise.all的用法也就一目了然了,它会让多个互不影响的Promise里的语句同时执行,等待所有的Promise语句全部执行完,然后执行then里的操作,返回给then的model是所有Promise里的resolve的值组成的一个数组。

    promise的异常处理

    同志们,接下来,嘿嘿嘿,继续讲故事:

    在填写个人信息的时候,小智的通知看到他填的年龄是8岁,于是指着他说:哈哈哈,你个智障,8岁还读幼儿园。。。小智恼羞成怒,和同桌大打了一架,打架中途,个人信息表也被同桌给撕了。这下子,班长小红苦恼了,老师可是说,要等每个人都写完再把他们所有的信息表收上来,这可怎么办啊。。。

    好了,我们暂且先不要管这个班里为什么会有这么多奇葩。我们来看逻辑,本来异步请求正常执行完,接下来就执行then里的resolve,那现在如果向服务器请求失败了,又会怎么样呢,总不能还是执行吧,直接看代码:

    var xiaozhi=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaozhi',age:'8'}
            var reason='打架,小智的信息表被撕了'
            if(reason){
                info=null
                reject(reason) 
            }else{
                resolve(info)
            }
            
        },5000)
    })
    xiaozhi.then(model=>{
        console.log(model)
    },error=>{
        console.log('提交个人信息表失败')
        console.log('原因是'+error)
    })
    

    这段代码执行结果如下:

    Paste_Image.png

    小智因为这个事情,被老师狠狠的批评了一顿,他不禁想,如果时光能够倒流,当初没有打这个架该多好啊。小伙伴们,你应该明白我的意思了,不打架,reason就是undefined,就会进入else里的语句,就能正常的输出给then的model了。实际情况正是如此,你很难保证你的请求就一定能够成功响应,如果没有成功请求到数据,你就应该拦住它,执行请求失败的情况的语句。
    可能有的小伙伴会错误的理解为,失败那不就是语句执行错误吗?这里一定要主要,这里的失败是指异步请求的失败,并不是js语句的执行失败。换句话说,这里的失败,是你自己定义的失败,如果你非要把请求成功定义为失败,那也会照样进入reject,而不会执行resolve。

    Promise.all的异常处理

    Promise.all的异常处理是什么样子呢,看代码:

    var xiaoming=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaoming',age:'5'}
            resolve(info)
        },2000)
    })
    var xiaogang=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaogang',age:'6'}
            resolve(info)
        },1000)
    })
    var xiaohua=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaohua',age:'7'}
            resolve(info)
        },5000)
    })
    var xiaozhi=new Promise((resolve,reject)=>{
        setTimeout(()=>{
            var info={name:'xiaozhi',age:'8'}
            var reason='打架,小智的信息表被撕了'
            if(reason){
                info=null
                reject(reason) 
            }else{
                resolve(info)
            }
        },1000)
    })
    
    var arr=[xiaoming,xiaogang,xiaohua,xiaozhi]
    Promise.all(arr).then(model=>{
        console.log(model)
    },error=>{
        console.log('收上来失败')
    })
    

    执行结果如下:

    Paste_Image.png

    1秒的时候,打印出“收上来失败”这句话,就好比

    小红把除了小智的其他人的信息表全部收上来了给老师,老师一看,少了小智的,说:少了小智的,重新发下去吧,这信息表不全,收上来我也不要。

    看到了吧,Promise.all就是保证所有的Promise都是成功的,只要有一个失败,它就执行reject,并且其他的哪怕是成功的Promise结果也都全部舍弃。这叫,一个都不能少!

    怎么样,小伙伴们,看了这篇文章,是否如同剥开了Promise身上的那层薄纱,将她看了个仔细呢(咳咳,别想歪。。。)。

    如果觉得文章不错,请在下方点赞支持我哦!

    参考资料

    MDN官方文档--Promise

    相关文章

      网友评论

      本文标题:趣味探索promise的秘密

      本文链接:https://www.haomeiwen.com/subject/lxcbittx.html