Promise的出现就是为了解决冗长的callback造成的不便如:
loadImg('a.jpg', function() {
loadImg('b.jpg', function() {
loadImg('c.jpg', function() {
console.log('all done!');
});
});
});
这样的代码不方便调试和观看 而且多了之后会越来越向右边推挤
Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。
字面上可以理解为“承诺”,就是说A调用B,B返回一个“承诺”给A,然后A就可以在写计划的时候这么写:当B返回结果给我的时候,A执行方案S1,反之如果B因为什么原因没有给到A想要的结果,那么A执行应急方案S2,这样一来,所有的潜在风险都在A的可控范围之内了。
- 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
- 一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
- promise必须实现
then
方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致 - then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。
promise也是CommonJS规范的一部分
最简单的Promise例子
function run(arg1, callback){
//....
callback || callback();
}
run(arg1, function(){
run(arg2, function(){
run(arg3, function(){
//.....一直嵌套下去
});
});
});
这是传统的方法
使用Promise
function run(arg){
return new Promise(function(resolve, reject){
//..... do something
if(success){
resolve(arg);
}else {
reject();
}
});
}
run(arg1)
.then(function(arg1){
//success
//do something
},function(arg1){
//wrong
//do something
})
.then(function(arg2){
//success
//do something
},function(arg2){
//wrong
//do something
})
.then(function(arg3){
//success
//do something
},function(arg3){
//wrong
//do something
});
简单来说,Promise规范就是定义一个function,这次不需要定义他的callback了,只需要return一个Promise对象,这个对象的构造函数接受一个匿名函数,匿名函数里定义这个function正常的功能,如果function执行成功了,调用resolve方法,这个方法可以接受一个参数,那么下个执行的函数将会接收到这个参数。反之,失败了定义执行reject。
调用时,执行该函数,在执行下一个函数时使用.then方法,正确执行对于的代码和错误执行对应的代码对应写入到then中,这样可以链式执行函数。
注意Promise执行之后,必须返回一个Promise对象才可执行下一个then方法,所以then方法中的函数一定返回Promise对象才可。
兼容性方面 高版本的主流浏览器都有内置的Promise对象,因为这个ES6的规范,较低版本的浏览器可以通过引入第三方的库来实现,例如bulebird。在nodejs中也无需npm模块,只需require(“Promise”);即可
下一个例子,一个动画效果 ,分别使用常规的方法实现,和Promise方法实现,对比下不同。
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<title>Promise animate</title>
<style>
.ball {
width:40px;
height:40px;
border-radius:20px;
}
.ball1 {
background: red;
}
.ball2 {
background: blue;
}
.ball3 {
background: yellow;
}
.ball4 {
background: black;
}
.ball5 {
background: pink;
}
.ball6 {
background: green;
}
</style>
<script type="text/javascript" src="./node_modules/bluebird/js/browser/bluebird.js"></script>
</head>
<body>
<div class="ball ball1" style="margin-left:0;"></div>
<div class="ball ball2" style="margin-left:0;"></div>
<div class="ball ball3" style="margin-left:0;"></div>
<div class="ball ball4" style="margin-left:0;"></div>
<div class="ball ball5" style="margin-left:0;"></div>
<div class="ball ball6" style="margin-left:0;"></div>
<script>
var ball1 = document.querySelector('.ball1');
var ball2 = document.querySelector('.ball2');
var ball3 = document.querySelector('.ball3');
var ball4 = document.querySelector('.ball4');
var ball5 = document.querySelector('.ball5');
var ball6 = document.querySelector('.ball6');
var Promise = window.Promise;
promiseAnimate(ball1, 100)
.then(function() {
return promiseAnimate(ball2, 200);
})
.then(function() {
return promiseAnimate(ball3, 300);
})
.then(function() {
return promiseAnimate(ball3, 150);
})
.then(function() {
return promiseAnimate(ball2, 150);
})
.then(function() {
return promiseAnimate(ball1, 150);
});
function promiseAnimate(ball, distance){
return new Promise(function(resolve, reject){
_animate();
function _animate(){
setTimeout(function(){
var marginLeft = parseInt(ball.style.marginLeft, 10);
if(marginLeft === distance){
resolve();
}else {
if(marginLeft < distance){
marginLeft++;
}else {
marginLeft--;
}
ball.style.marginLeft = marginLeft + "px";
_animate();
}
}, 13);
}
});
}
animate(ball4, 100, function(){
animate(ball5, 200, function(){
animate(ball6, 300, function(){
animate(ball6, 150, function(){
animate(ball5, 150, function(){
animate(ball4, 150, function(){
//alert("done");
});
});
});
});
});
});
function animate(ball, distance, cb){
setTimeout(function(){
var marginLeft = parseInt(ball.style.marginLeft, 10);
if(marginLeft === distance){
cb && cb();
}else {
if(marginLeft < distance){
marginLeft++;
}else {
marginLeft--;
}
ball.style.marginLeft = marginLeft + "px";
animate(ball, distance, cb);
}
}, 13);
}
</script>
</body>
</html>