ES6 Promise 实践 Tips Pt.1

Promises是一种成熟的异步编程模型, 它把程序中的特定后续状态抽象为Promise对象实例,让我们可以方便的对该未来状态进行编程. 在JavaScript世界中,Promises模型有着众多的实现. 虽然是同样或相似的思想,但每一种实现都会有一套自己的API或术语,这种分裂状态让我们对Promises的学习增添了不少困扰.

已经被各浏览器广泛实现的ECMAScript6中对Promise模型进行了精简 , 同时ES6 Promise模型也能在旧版本浏览器中被较好的模拟. 无论为了改善自己的异步编程技巧,还是使用基于ES6 Promise的新Web API(WebFont ,WebMidi)或是释放ES6 Generator的威力, 学习ES6 Promise都是必须的. ES6 Promise API大家可以通过MDNes6.ruanyifeng.com学习,我在下面会整理一些自己学习/使用Promise过程中的最佳实践.

NO.1增强异常处理意识

Promise模型消解“callback hall”的功用已经被之前众多介绍Promise的文章进行了充分强调,这里不继续累述.在实践中除了该项好处之外,Promise模型在 优化复杂控制流程,增强代码健壮性 这方面的作用对我们强调高可用性的电商系统也有着非常大的帮助. 从另一个角度看, ES6 Promise在出现异常时不会像jQuery Deferred 模型一样立即抛出异常,而是把Promise实例置为待Reject Handle状态,并向后传递.jQuery的策略有其优势,但这点上也提醒我们在使用ES6 Promise模型时应该更积极主动的思考异常策略,而不是等待最后调试时控制台抛出的大红叉.

No.2拆解 then 链

很多大牛在展示自己的Promise代码时喜欢用类似 .then().then().then().then()…… 这样的长长的then链来突出链式调用. 当其中每一步所需的业务逻辑简单或类似时,这种写法有其优势和美感.但当你的业务流程更为复杂或没有重复性时,这样的写法只会让你才出 “callback hall” 又入 “then hall”. ES6 Promise 模型中,无论是使用new或Promise.resolve ,Promise.reject构造实例,还是then或catch方法,你获得的都是另一个Promise对象实例.在编程时,你可以把每一次获得的Promise对象实例想象成未来的一个状态, 为每一个状态起一个能描述该状态的名字作为该Promise对象实例的变量名. 这样,你的Promise代码就能更清晰整洁,同时方便后续更细粒度的封装.

No.3使用catch来分离 resolve handler 和 reject handler

ES6 Promise的API几乎是所用Promise模型中最简单的( 构造函数之外,只有四个静态方法resolve,reject,all,race和两个实例方法then,catch ) ,而其中的catch方法实际上是 then(undefined, rejectHandlerFunc) 调用的语法糖 catch(func)的作用相等于在当前Promise对象实例上追加一次.then(undefined, func)调用 . 如此设计,可见API设计者对.catch 调用形式的重视.

No.4尽量为拆解后的每一步流程建立独立的异常处理机制

基于以上我们学到的then链拆解和catch使用技巧,我们可以如此这般来整理自己的ES6 Promise代码:

var domReadyPromise = new Promise(function(resolve,reject){
    $(document).ready(function() {
        resolve();
    })
});

var requestPromise  = domReadyPromise.then(function(){
    return lib.mtop.request({ api: "mtop.xxx", v: "1.0",data:"..."});
}).catch(function(e){
    console.error(e);
    throw new Error("mtop 请求错误");
});

var parserPromise = requestPromise.then(function(reqRes) {
    return parse(reqResres);
}).catch(function(e){
    console.error(e);
    throw new Error("parser error");
});

这样,我们既能让异步处理流变得清晰明了,又有了改善代码健壮性的机会.

No.5尝试从异常状态中恢复

在ES6 Promise模型中, 异常会让then方法把Promise实例置为待RejectHandle处理,并向后传递.就是说整个then链或拆解后的then链中的每次catch调用都有机会处理之前任意步骤时出现的异常. 同时,如任意 .catch(rejectHandler) 或 .then(undefined, rejectHandler) 中没有把截获的异常继续抛出或抛出新的异常. 后续的promise链路将恢复到正常待ResolveHandler处理的状态. 这样,就为我们提供了从异常中恢复的便捷途径:


var requestPromise  = domReadyPromise.then(function(){
    return lib.mtop.request({ api: "mtop.xxx", v: "1.0",data:"..."});
}).catch(function(e){
    console.error("mtop 请求错误");
    console.error(e);
    var defaultData = {.....};
    return defaultData;
});

var parserPromise = requestPromise.then(....);

如上面的代码,mtop请求时出现了错误,这时我们返回了默认数据,后续的流程将不会受mtop请求失败的影响继续进行.

其他

http://g.alicdn.com/mtb/lab-zikuan/0.0.7/promise/es6-promise.debug.js

http://g.alicdn.com/mtb/lab-zikuan/0.0.6/mtop/mtop_es6promise.js

参考