美文网首页
JS - Promise使用

JS - Promise使用

作者: _BuzzLy | 来源:发表于2020-04-27 18:59 被阅读0次

    在JavaScript中代码都是单线程执行的,因此JavaScript中所有的网络操作、浏览器事件都必须异步执行。在Promise之前JavaScript处理异步的方式都是回调函数。可以说callback的方式已是深入人心,那Promise又是解决什么问题的呢?看下面一段代码:

    $.get('/getList',function(){
        $.get('/getCount',function(){
            $.get('/getDetail',function(){
                //....
            })
        })
    })
    

    这段代码就是传统的callback方式处理异步,可以看到刚刚3级嵌套代码层级就已经比较乱了,如果再加上一些逻辑代码那简直是无法直视。这就是我们常说的回调地狱问题。代码可读性低,难以维护,无法复用。

    Promise解决回调地狱

    Promise的基本用法

    var promise = new Promise((resolve,reject)=>{
        setTimeout(function(){
            //这里异步操作已经执行完了,就可以通过resolve告诉外界可以进行其他操作了
            resolve('ok');
            //reject('no');
        },2000);
    })
    
    promise.then(res=>{
        console.log(res); // ok
    },err=>{
        console.log(err); // no
    })
    

    通过Promise处理异步,先执行异步操作,不关心如何处理结果,通过Promise对象的返回成功还是失败,在将来的某个时刻执行结果处理函数。代码变得扁平化,且易读易维护。

    • resolve && reject
      上面代码我们通过 resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
      而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)。

    Promise实现多层回调

    同样三级回调的代码我们再使用Promise重构一遍

    new Promise((resolve,reject)=>{
        $.get('/getList',res=>{
            resolve(res);
        });
    }).then(res=>{
        return new Promise((resolve,reject)=>{
            $.get('/getCount',res=>{
                resolve(res);
            });
        });
    }).then(res=>{
        return new Promise((resolve,reject)=>{
            $.get('/getDetail',res=>{
                resolve(res);
            })
        });
    }).then(res=>{
        //...
    });
    

    可以看到无论有多少层回调,都不用互相嵌套,只需要等待Promise对象“通知“执行即可。

    Promise.all

    当需要进行多步没有关联逻辑的异步操作时,可以使用Promise.all

    Promise.all([
        $.get('/getList'),
        $.get('/getCount'),
        $.get('/getDetail')
    ]).then(([data1,data2,data3])=>{
        //then回调的参数是一个数组,顺组数序就是异步操作书写顺序
        console.log(data1,data2,data3);
    },err=>{
        console.log(err);
    });
    
    • all方法中的参数应该是Promise对象,因为ajax函数返回的对象就是promise对象所以这里是可以执行的;
    • 此种方式只适用于异步操作之间无逻辑关联;
    • 不论异步操作执行顺序如何,最后都会按照书写顺序返回(data1,data2,data3是按照异步操作书写顺序返回);
    • 如果如其中一个出错就会执行错误回调;

    Promise.race

    race的用法与all相似,不同点就是all会等所有异步操作全部执行完后再执行then回调,而race中只要有一个异步操作执行完成就立刻执行then回调。

    Promise.race([
        new Promise(function(resolve, reject){
            setTimeout(function(){
                console.log('函数一执行!');
                resolve('11');
            }, 1000);
        }),
        new Promise(function(resolve, reject){
            setTimeout(function(){
                console.log('函数二执行!');
                resolve('22');
            }, 1200);
        })
    ]).then(result=>{
        console.log('then执行');
        console.log(result);
    },err=>{
        console.log(err);
    });
    
    //执行结果为:
    //函数一执行!
    //then执行
    //11
    //函数二执行!
    

    可以看到函数一执行明显要比函数二快,所以执行了函数一后立即执行了then回调,注意这时函数二依然会执行,但是执行后不会再触发then回调的执行。

    Promise错误处理

    • 第一种方式,通过then
    new Promise((resolve,reject)=>{
        //...
    }).then(resSuccess=>{
        //成功
    },resError=>{
        //失败
    });
    
    • 第二种方式,catch捕获
    new Promise((resolve,reject)=>{
        //...
    }).then(resSuccess=>{
        //成功
    }).catch(resError=>{
        //失败
    });
    

    注:catch方式更常用,因为不仅仅能捕获到reject传递的参数,还可以捕获到成功的回调中发生的错误。


    (*Deferred对象及其方法)

    jQuery 用 $.Deferred 实现了 Promise 规范。

    function fn1() {
       var def = $.Deferred();
       //执行异步操作
       setTimeout(function () {
           console.log('函数1!');
           def.resolve('函数1执行完毕回调');
           //def.reject('函数1执行完毕回调');
       }, 1000);
       return def.promise();
    }
    
    //then方法第一个参数接收成功回调,第二个参数是接收失败回调
    fn1().then(res => {
        console.log(res);
    },err=>{
        console.log(err);
    });
    

    $.Deferred()方法返回一个对象,我们可以称之为Deferred对象,该对象包含一些方法如:then、done、fail 等。
    jQuery就是用这个Deferred对象来实现Promise规范的。

    对于多级回调的情况也可以使用then()方法进行链式调用:

    function fn1() {
        var def = $.Deferred();
        //执行异步操作
        setTimeout(function () {
            console.log('执行函数1!');
            def.resolve('函数1执行完毕回调');
        }, 1000);
        return def.promise();
    };
    
    function fn2() {
        var def = $.Deferred();
        //执行异步操作
        setTimeout(function () {
            console.log('执行函数2!');
            def.resolve('函数2执行完毕回调');
        }, 1000);
        return def.promise();
    };
    
    fn1().then(res => {
        console.log(res);
        //可以链式调用的核心在于返回一个Deferred对象
        return fn2();
    }).then(res => {
        console.log(res);
    });
    
    //执行函数1!
    //函数1执行完毕回调
    //执行函数2!
    //函数2执行完毕回调
    

    done() && fail()
    done与fail的用法与then相似,实际就是一个语法糖。

    function fn1() {
       var def = $.Deferred();
       setTimeout(function () {
           console.log('执行函数1!');
           def.resolve('函数1执行完毕回调');
           // def.reject('函数1执行失败回调');
       }, 1000);
       return def.promise();
    };
    
    fn1().then(function (res) {
        console.log(res);
    }, function (err) {
        console.log(err);
    });
    
    fn1().done(function (res) {
        console.log(res);
    }).fail(function (err) {
        console.log(err);
    });
    

    always()
    Defferred对象上还有一个always方法,无论异步操作返回什么结果都会执行always回调。

     function fn1() {
        var def = $.Deferred();
        setTimeout(function () {
            console.log('执行函数1!');
            def.resolve('函数1执行完毕回调');
            // def.reject('函数1执行失败回调');
        }, 1000);
        return def.promise();
    };
    
    fn1().then(function (res) {
        console.log(res);
    }, function (err) {
        console.log(err);
    }).always(() => {
        console.log('无论成功失败都会进入这里');
    });
    

    由于Promise的出现,jQuery的Deferred对象已经很少使用了。一些其他用法在这也不一一举例了,如果有兴趣可以去官网详细了解。

    相关文章

      网友评论

          本文标题:JS - Promise使用

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