Promise是解决回调地狱的好工具,比起直接使用回调函数promise的语法结构更加清晰,代码的可读性大大增加。但是想要在真是的项目中恰当的运用promise可不是随便写个Demo这个简单的,如果运用不当反而会增加代码的复杂性。
如果辛辛苦苦写完代码,测试后发现不兼容IE6、7增么办?难道要推翻用回调函数重写?当然不是这样,轮子早就造好了。
我们可以使用es6-promise-polyfill。es6-promise-polyfill可以使用页面标签直接引入,可以通过es6的import方法引入(如果你是用webpack),在node中可以使用require引入,也可以在Seajs中作为依赖引入。
引入这个polyfill之后,它会在window对象中加入Promise对象。这样我们就可以全局使用Promise了。
es6-promise-polyfill 下载地址
我曾经看到过有人这样写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);
})
如果业务逻辑中存在大量的条件判断,那这样的业务并不适合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 。
参照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处理一次。
嘻唰唰