Promise项目实践与异常处理方式

Promise是解决回调地狱的好工具,比起直接使用回调函数promise的语法结构更加清晰,代码的可读性大大增加。但是想要在真是的项目中恰当的运用promise可不是随便写个Demo这个简单的,如果运用不当反而会增加代码的复杂性。

1. 使用Promise经常遇到的问题

1.1 老旧浏览器没有Promise全局对象增么办?

如果辛辛苦苦写完代码,测试后发现不兼容IE6、7增么办?难道要推翻用回调函数重写?当然不是这样,轮子早就造好了。

我们可以使用es6-promise-polyfill。es6-promise-polyfill可以使用页面标签直接引入,可以通过es6的import方法引入(如果你是用webpack),在node中可以使用require引入,也可以在Seajs中作为依赖引入。

引入这个polyfill之后,它会在window对象中加入Promise对象。这样我们就可以全局使用Promise了。

es6-promise-polyfill 下载地址

1.2 为什么我写的promise看起来和回调地狱差不多?

我曾经看到过有人这样写promise,这等同于回调地狱。

var wrongPromiseOuter =
    new Promise(function (resolve) {
        // 注意到了吗?这里在一个promise的内部创建了另一个promise
        setTimeout(function () {
            // 这里在promise的回调函数中又增加了一个回调函数 TT
            var wrongPromiseInner = new Promise(function (resolve) {
                setTimeout(function () {
                    resolve('innerResponse');
                }, 1000);
            })
            wrongPromiseInner.then(Response => {
                resolve('outerResponse');
            })
        }, 1000);
    })

wrongPromiseOuter.then(Response => {
    console.log(Response);
})

正确的做法是

var rightPromiseOuter =
    new Promise(function (resolve) {
        setTimeout(function () {
            resolve('outerResponse');
        }, 1000);
    });

rightPromiseOuter
.then(Response => {
    console.log(Response);
    return new Promise(function (resolve) {
                setTimeout(function () {
                    resolve('innerResponse');
                }, 1000);
            })
})
.then(Response => {
    console.log(Response);
})    

1.3 什么样的业务逻辑适合使用

如果业务逻辑中存在大量的条件判断,那这样的业务并不适合promise。Promise适合业务流程单一,不存在大量分支判断的情况。我们举例说明为什么过多的分支判断不适合promise。

这里我举了一个极端的例子,它会导致多个判断分支,使得每一次promise.then中的response会根据输入的参数而变化,这样我们处理response时就需要判断多种情况,代码就不易管理了。

function start(arg) {
    /* 
    这里是promise的开端 
    第一个异步造作会根据参数arg来选择执行不同的异步操作,
    并返回不同的结果
    */
    if (arg === 'conditionOne') {
        Promise.resolve('c1');
    }
    if (arg === 'conditionTwo') {
        Promise.resolve('c2');
    }
}

start('conditionOne')
.then(response => {
    /* 
    这是第二次异步操作,这里根据第二个判断条件otherCondition继续进入不同的异步操作 
    至此promise链中已经有了4条不同的分支
    */
    var otherCondition = 'ot1';

    if (response === 'c1') {
        if (otherCondition === 'ot1') {
            Promise.resolve('c11');
        } else {
            Promise.resolve('c12');
        }
    }

    if (response === 'c2') {
        if (otherCondition === 'ot1') {
            Promise.resolve('c21');
        } else {
            Promise.resolve('c21');
        }
    }
})
.then(response => {
    /* 越来越多的条件判断 */
})

针对这种情况有两种解决方案,思路一样的。一、我们可以创建多个promise调用链而不是把所有的情况写在同一个调用链中。二、可以用造好的轮子promise-conditional 。

1.4 如何进行异常处理?

参照promise的文档我们可以在reject回调和catch中处理异常。但是promise规定如果一个错误在reject函数中被处理,那么promise将从异常常态中恢复过来。这意味着接下来的then方法将接收到一个resolve回调。大多数时候我们希望发生错误的时候,promise处理当前的异常并中断后续的then操作。
我们先来看一个使用reject处理异常的例子

var promiseStart = new Promise(function(resolve, reject){
    reject('promise is rejected');
});

promiseStart
.then(function(response) {
    console.log('resolved');
    return new Promise(function(resolve, reject){
        resolve('promise is resolved');
    });
},function (error){
    console.log('rejected:', error);
    // 如果这里不抛出error,这个error将被吞掉,catch无法捕获异常
    // 但是如果抛出error,这个error会被下一个then的reject回调处理,这不是我们想要的
    throw(error); 
})
.then(function (response){
    console.log('resolved:', response);
},function (error){
    console.log('rejected:', error);
    throw(error);
})
.catch(function(error) {
    console.log('catched:', error);
})

/* 
 输出:
 rejected: promise is rejected
 rejected: promise is rejected
 catched: promise is rejected
 */

在这个例子中reject回调处理了异常,但是它并不能中断后续then操作。第二个then中的reject被触发了。

而正确的做法是,不要使用reject!让错误直接到catch中捕获。

var promiseStart = new Promise(function(resolve, reject){
    reject('promise is rejected');
});

promiseStart
.then(function(response) {
    console.log('resolved');
    return new Promise(function(resolve, reject){
        resolve('promise is resolved');
    });
})
.then(function (response){
    console.log('resolved:', response);
})
.catch(function(error) {
    console.log('catched:', error);
})

/* 
 输出:
 catched: promise is rejected
 */

这样发生了错误后后续的then不会被调用,错误也只被catch处理一次。

嘻唰唰

你可能感兴趣的:(Web前端)