Promise 的含义
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
上面代码中,p1
和p2
都是 Promise 的实例,但是p2
的resolve
方法将p1
作为参数,即一个异步操作的结果是返回另一个异步操作。
注意,这时p1
的状态就会传递给p2
,也就是说,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
上面代码中,调用resolve(1)
以后,后面的console.log(2)
还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolve
或reject
以后,Promise 的使命就完成了,后继操作应该放到then
方法里面,而不应该直接写在resolve
或reject
的后面。所以,最好在它们前面加上return
语句,这样就不会有意外。例如:
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
then
方法返回的是一个新的Promise
实例(注意,不是原来那个Promise
实例)。因此可以采用链式写法,即then
方法后面再调用另一个then
方法。
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
面的代码使用then
方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
采用链式的then
,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise
对象(即有异步操作),这时后一个回调函数,就会等待该Promise
对象的状态发生变化,才会被调用。
getJSON("/post/1.json").then(function(post) {
return getJSON(post.commentURL);
}).then(function funcA(comments) {
console.log("resolved: ", comments);
}, function funcB(err){
console.log("rejected: ", err);
});
上面代码中,第一个then
方法指定的回调函数,返回的是另一个Promise
对象。这时,第二个then
方法指定的回调函数,就会等待这个新的Promise
对象状态发生变化。如果变为resolved
,就调用funcA
,如果状态变为rejected
,就调用funcB
。
如果采用箭头函数,上面的代码可以写得更简洁。
getJSON("/post/1.json").then(
post => getJSON(post.commentURL)
).then(
comments => console.log("resolved: ", comments),
err => console.log("rejected: ", err)
);
Promise经典例子介绍
以img加载为例
1.传统的callback写法
var src = "https://www.baidu.com/img/bd_logo1.png";
function imgload(src, callback, fail) {
var img = document.createElement('img');
img.onload = function () {
callback(img);
};
img.onerror = function () {
fail();
};
img.src = src;
}
imgload(src, function (a) {
console.log(a.width);
}, function () {
console.log('加载失败');
});
2.promise改进写法
var src="https://www.baidu.com/img/bd_logo1.png";
function imgload(src) {
const promise=new Promise(function (resolve,reject) {
var img=new Image();
img.onload=function(){
console.log(2);
resolve(img);
};
img.onerror=function () {
reject();
};
console.log(6);
img.src=src;
});
console.log(1);
return promise;
}
var result=imgload(src);
result.then(function (img) {
console.log(`图片的宽度为${img.width}`);
},function () {
console.log("图片加载失败");
});
result.then(function (img) {
console.log(`图片的高度为${img.height}`)
});
result.then(function (img) {
console.log(`图片的src为${img.src}`)
});
对比可以得出,promise好处颇多,不仅逻辑结构清晰,而且可以链式调用,解决了多层回调嵌套地狱问题!
var test = (resolve, reject) => {
var num = Math.random() * 10;
setTimeout(function() {
if(num < 5) {
resolve('YES');
} else {
reject('NO');
}
}, 1000);
};
var p1 = new Promise(test);
var p2 = p1.then((success) => {
console.log(success)
console.log(a)
}, (fail) => {
console.log(fail)
}).catch(()=>{
console.log('异常')
}).finally(()=>{
console.log('123')
});
//就是说then有第二个参数就走reject不会走catch,否则走catch,但第一个或第二个参数里有报错,会被catch捕获
//再总结一句,有reject优先第二个参数,catch的作用是没有第二个参数就干reject的活,并且自己会捕获两个参数里的异常报错
文章参考阮一峰老师的ECMAScript 6 入门 详情参考 http://es6.ruanyifeng.com/#docs/promise