ES6 Promise对象的用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例

new 一个Promise对象

        var p = new Promise(function(resolve,reject){
            //做一些异步操作
            setTimeout(function(){
                console.log('执行完成');
                resolve('成功输出');
            },2000);
        });

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,有JavaScript引擎提供,不用自己部署

resolve函数的作用 : 将Promise对象的状态从'未完成'变为'成功',在异步操作成功时调用,并将异步操作的结果作为参数传递出去

reject函数的作用:将Promise对象的状态从'未完成'变为'失败',在异步操作失败时调用,并将异步操报出的错误作为参数传递出去

resolve是将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected

在上面的代码中,我们执行了一个异步操作,也就是setTimeout,2秒后,输出“执行完成”,并且调用resolve方法。

运行代码,会在2秒后输出“执行完成”。但是我只是new了一个对象,并没有调用它,我们传进去的函数就已经执行了,这是需要注意的一个细节。所以我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数

        function timeout() {
            var p = new Promise(function(resolve,reject){
            //做一些异步操作
            setTimeout(function(){
                console.log('执行完成');
                resolve('成功输出');
            },2000);
        });
        return p
        }
        timeout()

所以包装这么一个函数有什么用?resolve('成功输出')是有什么用?

观察函数,我们发现 , 在我们包装好的函数最后,会return出Promise对象,也就是说,执行这个函数我们得到了一个Promise对象。知道Promise对象上有then、catch方法吧?这就是强大之处了,看下面的代码:

       timeout().then(function(data){
            console.log(data); 
            //后面可以用传过来的数据做些其他操作
        })

在timeout()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在timeout中调用resolve时传的的参数。运行这段代码,会在2秒后输出“执行完成”,紧接着输出“成功输出”。

在这里看来,then里面的函数就和以前我们所理解的回调函数一个意思,但是,如果有多层回调呢?看下面代码

     setTimeout(function(){
       timeout(function(){
         setTimeout(function(){
           timeout(function(){
             setTimeout(function(){
               timeout();
               },2000);
             });
          }, 2000);
        });
      }, 2000);

以上代码就是传说中的回调地狱,如果有多层业务逻辑嵌套的话,不仅会使代码阅读困难,而且后面维护起来也是难点

看看Promise怎么用

        timeout().then(function(data){
            console.log(data);
            return timeout2();
            //后面可以用传过来的数据做些其他操作
        }).then(function(data){
            console.log(data);
            return timeout3();
        }).then(function(data){
            console.log(data);
        })

这样能够按顺序,每隔两秒输出每个异步回调中的内容,在timeout2中传给resolve的数据,能在接下来的then方法中拿到。运行结果如下:

        function timeout() {
            var p = new Promise(function(resolve,reject){
            //做一些异步操作
            setTimeout(function(){
                console.log('执行完成');
                resolve('成功输出');
            },2000);
        });
        return p
        }
        function timeout2() {
            var p = new Promise(function(resolve,reject){
            //做一些异步操作
            setTimeout(function(){
                console.log('执行完成2');
                resolve('成功输出2');
            },2000);
        });
        return p
        }
        function timeout3() {
            var p = new Promise(function(resolve,reject){
            //做一些异步操作
            setTimeout(function(){
                console.log('执行完成3');
                resolve('成功输出3');
            },2000);
        });
        return p
        }

reject的用法

将Promise对象的状态从'未完成'变为'成功',在异步操作成功时调用,并将异步操作的结果作为参数传递出去

举个栗子:

    function getNum(){
        var p = new Promise(function(resolve,reject){
            setTimeout(function(){
                var num = Math.ceil(Math.random()*10); //生成1-10的随机数
            if(num>5){
                resolve(num)
            }else{
                reject('数字不对哦')
            }
            },2000)
        })
        return p;
    }
    getNum().then(function(data){
        console.log('resolved');
        console.log(data)
    },function(data){
        console.log('rejected')
        console.log(data);

    })

getNum函数用来异步获取一个数字,2秒后执行完成,如果数字大于5,我们认为是“成功”了,调用resolve修改Promise的状态。否则我们认为是“失败”了,调用reject并传递一个参数,作为失败的原因。

then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。多次运行这段代码,会得到不同的状态

catch的用法

catch方法是then(null,rejection)的别名,用于指定发生错误时的回调函数,所以上面也可以这么写

    getNum().then(function(data){
        console.log('resolved');
        console.log(data)
    }).catch(function(data){
        console.log('rejected')
        console.log(data);
    })

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

    getNum().then(function(data){
        console.log('resolved');
        console.log(data);
        console.log(sss);   //未定义
    }).catch(function(data){
        console.log('rejected')
        console.log(data);
    })

在resolve的回调中,我们console.log(somedata);而somedata这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了

也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能。

all的用法

Promise.all方法用于将多个Promise实例包装成一个新的Promise实例

    Promise.all([timeout(),timeout2(),timeout3()]).then(function(results){
        console.log(results);
    })

用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。

race的用法

「谁跑的快,以谁为准执行回调」,这就是race方法,race的用法与all一样,我们把上面timeout2的延时改为1秒来看一下

    Promise.race([timeout(),timeout2(),timeout3()]).then(function(results){
        console.log(results);
    })

我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作

//请求某个图片资源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = 'xxxxxx';
    });
    return p;
}

//延时函数,用于给请求计时
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('图片请求超时');
        }, 5000);
    });
    return p;
}

Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});

requestImg函数会异步请求一张图片,我把地址写为"xxxxxx",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。运行结果如下:

猜你喜欢

转载自blog.csdn.net/weixin_41910848/article/details/81535733