1.什么是Promise
所谓 Promise,就是JS的一个对象,用来传递异步操作结果的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供了统一的 API,用来对异步调用需要的两种结果(成功与失败)进行处理。
Promise 使用示例:
1.使用new Promise(fn)创建并得到一个Promise对象,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve方法和reject方法。
2.在 fn 中指定异步等处理
(1) 如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从"未完成" 变为"成功"(即从 pending 变为 resolved)。
(2) 如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从"未完成"变为"失败"(即从 pending 变为 rejected)。
3.通过promise的then方法,对不同状态的promise进行处理。
var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function onFullfilled(value) {
// success
}, function onRejected(error) {
// failure
});
2.使用Promise与正常异步回调的区别
在callback的模型里边,我们假设需要执行一个异步队列,代码看起来可能像这样。
var adminIndex = function(params, callback) {
storeAdmin.getApiTokens(function(err, tokens){
if ( err ) {
callback(err);
return;
}
storeAdmin.getApiServices(function(err, apiServices){
if ( err ) {
callback(err);
return;
}
storeAdmin.getSocketioServices(function(err, socketioServices){
if ( err ) {
callback(err);
return;
}
callback(0, {status : true, data : {api_tokens : tokens,api_services : apiServices,socketio_services : socketioServices}});
});
});
});
};
这就是常说的回调金字塔,当异步的任务很多的时候,维护大量的callback将是一场灾难。而Promise则是把类似的异步处理对象和处理规则进行规范化, 并采用统一的接口来编写,除promise对象规定的方法(这里的 then 或 catch )以外的方法都是不可以使用的, 而不会像回调函数方式那样可以自己自由的定义回调函数的参数,从而必须严格遵守固定、统一的编程方式来编写代码。各个阶段抛出的错误都可以在一个catch下统一处理,同时promise的then方法会返回另一个promise对象,以便于形成promise管道,这种返回promise对象的方式能够支持开发人员把异步操作串联起来,从而避免了回调金子塔的状况,达到一个"线性编码、异步执行"的效果。
3.用.then 或 .catch 添加promise对象的处理函数
(1).Promise 状态:
Promise存在三个状态,分别是Fulfilled、 Rejected、 Pending。其创建后状态的初始化值为pending,当promise中的异步处理任务得到结果后,设置对应的Promise状态。而在使用Promise.resolve方法的时候,可以认为它的作用就是将异步操做返回的结果填充(Fulfilled)到promise对象并返回这个promise对象。
之后根据promise的不同状态,使用then方法对其进行相应的处理。
asyncFunction().then(function onFulfilled(value) {
console.log(value);
}, function onRejected(error) {
console.log(error);
});
(2).Promise catch:
Promise提供then方法加载回调函数,或者使用catch方法捕捉执行过程中抛出的错误。
aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
throw new Error(’taskB has a bug’);
}).catch(function onRejected(error){
console.log(error);
});
以上数据处理流程相当于:
aPromise.then(function taskA(value){
// task A
}).then(function taskB(vaue){
throw new Error(’taskB has a bug’);
}).then(undefined, function onRejected(error){
console.log(error);
});
使用promise中的 .then方法 、 .catch方法创建一个异步获取网络数据的流程
我们可以对过程中的匿名函数进行封装, 大概像这样: getURL(URL).then(parseJson(response)).then(checkResCode(json)).then(handleResponse(json)).catch(....)??梢苑⑾终舛未虢醮锏搅俗匀挥镅缘某潭?,十分优雅。
3.Promise chain:
每次调用promise的then方法后都会返回一个新的promise对象,通过这种特性我们可以创建对应的promise chain(类似RAC中的signalOperation, 每个signal的operation方法都返回一个signal)。
function taskA() {
console.log("Task A");
}
function taskB() {
console.log("Task B");
}
function onRejected(error) {
console.log("Catch Error: A or B", error);
}
function finalTask() {
console.log("Final Task");
}
var promise = Promise.resolve();
promise.then(taskA)
.then(taskB)
.catch(onRejected)
.then(finalTask);
对应的流程图:
4.Promise与RAC的异同
以上的promise处理流程如果使用RAC来表示的话可以表示成下图代码:
RAC的CreateSignal 类似于创建一个promise;sendNext, sendError 可以看成resolve与reject操作;RAC中的try->try->catch 与 promise中的then->then-> catch相似。从相同的代码格式我们可以看出,RAC与Promise都是在初始化的时候,就规划好数据处理流程,使得代码更加直观与优雅,当然在两者比较举例的同时我们可以排除掉一些不影响宏观层面上不同点,比如:RAC信号需要被订阅,而Promise在创建好后即执行内部代码等。
除去以上的异同点,非常值得关注的就是RAC与Promise都采用了高阶函数的思想。何为高阶函数?高阶函数是至少满足以下两个条件其中之一的函数:1.接收一个或多个函数作为输入。2.输出一个函数。(同时FP下高阶函数都有一个共性:可以链式调用)。比如promise中的then, catch使用了函数参数, RAC的try, catch使用了block就是很好的例子。再比如swift中提供的filter, reduce 与map等。那么它们使用高阶函数有什么好处呢?能够将现有的代码由许多粒度非常小的功能函数来组合,如果将粒度缩到足够小的话,这些小粒度的功能函数能够达到极强的通用性,有通用性就可以对其进行下沉, 使其能够为其它??槭褂谩W詈?,原先的信号流或者数据流就会由大量的小粒度功能函数构成,从而把繁琐而又乏味的任务抽象出来,这将使代码显的直观与优雅。
修改后的RAC代码:
修改后的promise代码:
RAC与Promise还有一个相同的优点,纵向加载。在SDK写网络接口回调的时候,往往我们需要验证各种数据的正确性,condition1, condition2, condition3...每个验证条件都是一个if-else, 条件一多,if-else的多层嵌套让人眩晕,不仅不利于它人review,对于编码者而言,时间一久也会头大。同时多层的嵌套也非常容易漏掉抛往上层的错误通知。这种通过嵌套if-else的写法称之为横向发展,这样的代码很快就会乱成一团,无法管理。