为什么要有Promise
由于js本身的单线程机制,导致js的网络操作等必须通过异步执行,而早期的异步执行方式都是通过callback(即回调函数)完成的,例如:
function callback() {
console.log('回调函数执行');
}
console.log('开始异步前');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('开始异步后');
//观察输出为
开始异步前
开始异步后
(等待1秒后)
回调函数执行
当业务逻辑需要我们执行一连串的回调时,代码可能像下面这样:
function callback(count, fun) {
setTimeout(function () {
console.log('回调成功' + count + '次');
fun(count);
}, 1000);
}
callback(1, function (count) {
callback(count + 1, function (count) {
callback(count + 1, function (count) {
callback(count + 1, function (count) {
callback(count + 1, function (count) {
console.log(count + '次回调结束');
})
})
})
})
})
//观察输出为
(等待1秒后)
回调成功1次
(等待1秒后)
回调成功2次
(等待1秒后)
回调成功3次
(等待1秒后)
回调成功4次
(等待1秒后)
回调成功5次
5次回调结束
这段代码就是臭名昭著的邪恶金字塔(Pyramid of Doom),当异步的任务很多的时候,维护大量的callback将是一场灾难。
想象一下如果在callback之内必须要执行不同的处理逻辑,比如success和fail:
function callback(mark, options) {
var options = options || {}
var successFun = options.success;
var failFun = options.fail;
setTimeout(function () {
if (mark) {
successFun('success');
} else {
failFun('fail');
}
}, 1000);
}
callback(true, {
success: function (data) {
console.log(data);
callback(true, {
success: function (data) {
console.log(data);
callback(false, {
success: function (data) {
console.log(data);
},
fail: function (data) {
console.log(data);
}
})
},
fail: function (data) {
console.log(data);
}
})
},
fail: function (data) {
console.log(data);
}
})
//观察输出为
(等待1秒后)
success
(等待1秒后)
success
(等待1秒后)
fail
这样的代码看起来简直一团糟,可读性极差,是不是能有一种更简介明了的处理异步方法时序的写法呢,于是乎 Promise横空出世了
什么是Promise
所谓Promise,字面上可以理解为“承诺”,就是说A调用B,B返回一个“承诺”给A,A就可以根据B的"承诺"决定自己要执行什么逻辑,比如当B返回想要的结果时,A执行planA,否则执行planB。如果后续还有planC、palnD等依赖planA的结果,可以继续在planA上进行一个"承诺",携程代码的结构大致如下:
var A = function() {
B.then(planA, planB)
.then(planC, planD)
.then(planE, planF)
}
A();
这种链式写法的好处在于,逻辑结构清晰明朗,一眼看出谁先谁后
Promise基本概念
Promise在ES6中被统一规范,目前许多高级浏览器已经直接支持(Chrome 32、Opera 19、Firefox 29、Safari 8 和 Microsoft Edge 起,promise 默认启用),且有许多开源库的实现,例如:
先来认识下Promise/A+规范:
-
一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
-
一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
-
promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
-
then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
用Promise编写一个需要串行执行的异步计算代码
function plus(input) {
return new Promise((resolve, reject) => {
console.log('执行:' + input + ' + ' + input);
setTimeout(resolve, 1000, input + input);
});
}
function multiply(input) {
return new Promise((resolve, reject) => {
console.log('执行:' + input + ' * ' + input);
setTimeout(resolve, 1000, input * input);
});
}
var arithmetic = new Promise((resolve, reject) => {
console.log('开始进行计算:');
resolve(6)
});
arithmetic.then(multiply)
.then(plus)
.then(multiply)
.then(plus)
.then(function (result) {
console.log('计算结果:' + result);
});
//观察输出为
开始进行计算:
(等待1秒后)
执行:6 * 6
(等待1秒后)
执行:36 + 36
(等待1秒后)
执行:72 * 72
(等待1秒后)
执行:5184 + 5184
(等待1秒后)
计算结果:10368
用Promise重写之前的callback
function callback(mark) {
return new Promise((resolve, reject) => {
setTimeout(function () {
if (mark) {
resolve('success');
} else {
reject('fail');
}
}, 1000);
})
}
callback(true).then(function (success) {
console.log(success);
return callback(true);
}, function (fail) {
console.log(fail);
}).then(function (success) {
console.log(success);
return callback(false);
}, function (fail) {
console.log(fail);
}).then(function (success) {
console.log(success);
}, function (fail) {
console.log(fail);
})
//观察输出为
(等待1秒后)
success
(等待1秒后)
success
(等待1秒后)
fail
then 执行后会 return 一个新的 Promise 对象,以实现链式调用,你可以显式的 return 一个 Promise 对象,若不显式的 return 则会 return 一个以 undefined 值 resolve 的 Promise 对象。 返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return 1 等价于 return Promise.resolve(1)。
示例如下:
callback(true).then(function (success) {
console.log(success);
}, function (fail) {
console.log(fail);
}).then(function (success) {
console.log(success);
}, function (fail) {
console.log(fail);
}).then(function (success) {
console.log(success);
}, function (fail) {
console.log(fail);
})
//观察输出为
(等待1秒后)
success
undefined
undefined
更加详细的解释和示例可参考: Javascript Promise 简介