• JavaScript---Promise


      什么是Promise?

         Promise是ES6新增的对象。他是构造函数,通过构造实例来使用他的方法。 var p = new Promise();

       Promise是干什么用的

        用来传递异步操作的消息(如ajax请求),它代表了某个未来才会知道结果的事件(通常是一个异步操作)

     

       Promise的3种状态:

           1.Pending(未完成)---译为‘即将发生的’,可以理解为Promise对象创建实例的初始状态;

            2. Resolved(成功)---译为‘已解决的’,可以理解为成功的状态;

            3.Rejected(失败)--译为‘拒绝的’,可以理解为失败的状态。

       Promise对象的特点

            1.Promise的状态不受外界影响。只有异步操作的结果,才可以决定当前是什么状态,其他任何操作都无法改变他的状态。

            2.一旦状态发生改变,就不会再变,任何时候都可以得到这个结果。Promise对象的的状态改变,只有两种情况:1.Pending变为Resolved, 2.Pending变为Rejected。只要这两种情况发生其中一个,另一个是不会发生的。因为状态会凝固,即使你还想添加回调函数试图改变结果,也不起作用。

        什么时候用promise?函数回调多层嵌套(有依赖关系)

          来看一个demo:多个ajax的请求(存在依赖)

          常用的方式:使用函数嵌套

    $.ajax({
                    url:'http://localhost/api/req1.php',
                    success:function(str1){
                        console.log(str1);
    
                        $.ajax({
                            url:'http://localhost/api/req2.php',
                            success:function(str2){
                                console.log(str2);
    
                                $.ajax({
                                    url:'http://localhost/api/req3.php',
                                    success:function(str3){
                                        console.log(str3);
    
                                        document.body.innerHTML = str1 + str2 + str3
                                    }
                                })
                            }
                        })
                    }
                })

      ajax的多层嵌套会造成阻塞,只有前一个请求成功了,下一个请求才会开始执行,万一前一个请求执行失败,下一个请求永远无法执行,造成页面‘假死’。

      再来看promise是如何处理的?

        Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。

        如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved)。

        如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。

    var p1 = new Promise(function(resolve,reject){
                // 这时的状态:pending
                $.ajax({
                    url:'http://localhost/api/req1.php',
                    success:function(str1){
                        // console.log(str1);
                        
                        
                        //把promise对象的状态改成resolved
                        //只要状态被修改成resolved,就不会再改变
                        resolve(str1);
                    }
                })
            });
    
            var p2 = new Promise(function(resolve,reject){
                $.ajax({
                    url:'http://localhost/api/req2.php',
                    success:function(str2){
                        // console.log(str2);
    
                        resolve(str2);
                    }
                })
            });
    
            var p3 = new Promise(function(resolve,reject){
                $.ajax({
                    url:'http://localhost/api/req3.php',
                    success:function(str3){
                        // console.log(str3);
    
                        resolve(str3)
                    }
                });
            });
    
    
            // 等3个请求都成功后
            // 把数据写入页面
            var pAll = Promise.all([p1,p2,p3]);
    
            pAll.then(function(data){
                console.log('所有:',data);
            });
    
    
            // 返回第一个完成的数据
            var pFast = Promise.race([p1,p2,p3]);
    
            pFast.then(function(data){
                console.log('手速最快:',data);
            });

        再来看具体的实例:

          比如:获取当前城市的天气预报API-->获取当前城市API-->获取当前城市的ip地址API

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>多个ajax请求</title>
        <script src="js/jquery-3.1.1.js"></script>
        <script>
        $(function(){
            /*
                * http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=58.248.240.46
    
                * http://ip.taobao.com/service/getIpInfo.php?ip=58.248.240.46
    
                
                ajax依赖数据解决方案
                * ajax嵌套
                    * 层级太深,难以维护
                * 同步请求
                    * 影响其他代码的执行
                * ES6 Promise
                    *     
             */
            
            var currentCity;
    
    
            // 通过IP获取当前城市
            var cityPromise = new Promise(function(resolve,reject){
                $.ajax({
                    url:'../getCity.php',
                    // async:false,
                    dataType:'json',
                    success:function(data){
                        console.log(data);
                        currentCity = data.data.city.replace(/市$/,'');
                        console.log(currentCity);
    
                        resolve(currentCity);
                    },
                    error:function(){
                        reject('请求失败')
                    }
                });
            });
    
            cityPromise.then(function(city){
                console.log('成功',city);
    
                $.ajax({
                    url:'http://wthrcdn.etouch.cn/weather_mini',
                    dataType:'json',
                    data:{city:currentCity},
                    success:function(data){
                        console.log(data)
                    }
                });
            },
            function(){
                console.log('失败')
            });
    
    
            
        })
        </script>
    </head>
    <body>
        
    </body>
    </html>

      在chrome中显示:

      补充:Promise对象的内置方法

        Promise.all(p1,p2,p3...)---将多个Promire 实例包装成一个新的Promise实例 注:所有的参数中的promise状态都为resolved时,新的promise状态才为

    resolved,一个是rejected,新的promise状态为rejected。

        Promise.race()---竞速模式  返回最先执行完的resolved结果。

        原型方法:Promise.prototype.then(successFn[,failFn]) ---Promise实例生成以后,可以用then方法分别指定Resolved状态和Rejected状态的回调函数。并根据Promise对象的状态来确定执行的操作:

            1.resolved时执行第一个函数successFn。

            2.rejected时执行第二个函数failFn。

    var p = new Promise(function(resolve, reject){
        //ajax请求
        ajax({
            url:'xxx.php',
            success:function(data){
                resolve(data)
            },
            fail:function(){
                reject('请求失败')
            }
        });
    });
    
    //指定Resolved状态和Rejected状态的回调函数
    //一般用于处理数据
    p.then(function(res){
        //这里得到resolve传过来的数据
    },function(err){
        //这里得到reject传过来的数据
    })

          Promise对象就暂告一段落, 细节以后慢慢补充         

                                           2017-3-27   15:20

    ---------------------------------------------------------------------------------更新!!!!----------------------------------------------------------------------------

    前天面试的时候,面试官有问到promise用法,但是好久没用了。竟然忘了!!!!! 这充分说明了,回顾知识是多么重要!!! 你永远不知道,你下一秒会不会用到。

      回到正题:

        前面说道,创建一个promise对象,只需“new”一个就好了。

     var p1 = new Promise(function(resolve,reject){
                setTimeout(function () {
                    alert('666');
                },2000);
            })

      试着执行这一段代码,2秒后弹出框“666”。 感觉有点怪怪的,我只是“new”出来一个promse对象而已,并没有调用啊,他怎么自动执行了?注意这个细节。一般地,promise对象放到一个函数里面,不要让他自动执行。

      

    function runAsync() {
                var p1 = new Promise(function (resolve, reject) {
              // 异步操作的代码 setTimeout(
    function () { alert('666'); }, 2000); }); return p1 } runAsync();

      上面的写法,返回实例promise。然后直接可以调用then() 方法了。 then方法接收一个函数作为参数,并且会拿到我们在runAsync中调用resolve时传的的参数。

    来,感受一下:

      

     function runAsync() {
                var p1 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                       console.log('666');
                        var obj = {
                            name:'jjk',
                            age:18
                        }
                        resolve(obj)
                    }, 2000);
                });
                return p1
            }
            runAsync().then(function(data){
                console.log(data);
            })

      在chrome中显示:

      

     then里面的函数就跟我们平时的回调函数一个意思,能够在runAsync这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。看到这里,疑问就来了,回调函数不是也可以做到这一点么?

    function runAsync(callback){
                 setTimeout(function () {
                        console.log('666');
                        var obj = {
                            name:'jjk',
                            age:18
                        }
                       callback(obj);
                    }, 2000);
            }
            runAsync(function(data){
                console.log(data);
            })

      看来Promise不怎么样啊。别急,试想一下,有多层回调该怎么办?如果callback也是一个异步操作,而且执行完后也需要有相应的回调函数,该怎么办呢?总不能再定义一个callback2,然后给callback传进去吧。而Promise的优势在于,可以在then方法中继续写Promise对象并返回,然后继续调用then来进行回调操作。从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:

    function runAsync1() {
                var p1 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        console.log('111');
    
                        resolve('第一次')
                    }, 2000);
                });
                return p1
            };
            function runAsync2() {
                var p2 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        console.log('222');
                        resolve("第二次")
                    }, 2000);
                });
                return p2
            };
            function runAsync3() {
                var p3 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        console.log('333');
                        resolve("第三次")
                    }, 2000);
                });
                return p3
            };
    
            runAsync1().then(function (data) {
                console.log(data);
                return runAsync2()
            })
            .then(function (data) {
                    console.log(data);
                    return runAsync3()
                })
            .then(function (data) {
                    console.log(data);
                })

      在chrome中显示:

        

      在then方法里面直接返回promise实例,然后继续链式调用。那么,如果我不返回实例,而是直接返回数据,还能链式调用吗?

      

     function runAsync1() {
                var p1 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        console.log('111');
    
                        resolve('第一次')
                    }, 2000);
                });
                return p1;
            };
            function runAsync2() {
                var p2 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        console.log('222');
                        resolve("第二次")
                    }, 2000);
                });
                return p2;
            };
            function runAsync3() {
                var p3 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        console.log('333');
                        resolve("第三次")
                    }, 2000);
                });
                return p3;
            };
    
            runAsync1().then(function (data) {
                console.log(data);
                return '直接返回数据看看'
            })
            .then(function (data) {
                    console.log(data);
                    return runAsync3()
                })
            .then(function (data) {
                    console.log(data);
                })

      在chrome中显示

    结果显而易见,返回数据之后,还可以调用then方法,可then方法不是promise的方法吗? 答案是这样的:then方法返回数据时,会把数据包装成promise对象,以供下一个then调用。

      事实上,我们前面的例子都是只有“执行成功”的回调,还没有“失败”的情况,reject的作用就是把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。

      

    function runAsync() {
                var p1 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        var num = Math.ceil(Math.random() * 10);
    
                        if (num <= 5) {
                            resolve(num);
                        } else {
                            reject('warming')
                        }
                    }, 1000)
                });
                return p1;
            }
            runAsync().then(function (data) {
                console.log('resolved');
                console.log(data);
            }, function (reason, data) {
                console.log('rejected');
                console.log(reason);
                console.log(data);
            })

    在chrome中显示:

       或者

     可以看出then方法接收2个函数作为参数,第一个参数是状态成功时执行,第二个参数是状态失败后执行,该函数的第一个参数就是reject的数据。

      说完了then方法,还要说说其他方法

      catch,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调,用法是这样:

    function runAsync() {
                var p1 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        var num = Math.ceil(Math.random() * 10);
    
                        if (num <= 5) {
                            resolve(num);
                        } else {
                            reject('warming')
                        }
                    }, 1000)
                });
                return p1;
            }
            runAsync()
            .then(function (data) {
                console.log('resolved');
                console.log(data);
            })
            .catch(function(err){
                console.log('rejected');
                console.log(err);
            })

      不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。来看代码:

    function runAsync() {
                var p1 = new Promise(function (resolve, reject) {
                    setTimeout(function () {
                        var num = Math.ceil(Math.random() * 10);
    
                        if (num <= 5) {
                            resolve(num);
                        } else {
                            reject('warming')
                        }
                    }, 1000)
                });
                return p1;
            }
            runAsync()
            .then(function (data) {
                console.log('resolved');
                console.log(data);
                console.log(num);
            })
            .catch(function(err){
                console.log('rejected');
                console.log(err);
            })

       当进入resolve状态时,在chrome中显示:

      

      这个跟try/catch 类似。

  • 相关阅读:
    2022年阿里内部Java岗面试offer直通车,年薪50W不是梦
    2022年腾讯首发Java岗分布式面试真题,助力金三银四我是认真的
    open()函数在if条件中时的问题
    关闭stdout后打开stdout
    container_of
    tmpnam函数和tmpfile函数
    dup和dup2函数
    vim命令
    FILE结构体的定义
    jQuery 完整 ajax示例
  • 原文地址:https://www.cnblogs.com/first-time/p/6626912.html
Copyright © 2020-2023  润新知