首先是对于Promise的一个简单的介绍,这个东西是什么,可以用来做什么。
Promise是ES6中实现语言标准的异步编程解决方案,是对于回调函数和事件的一种新的处理方案。
当然,说到这里,如果你对于Promise没有接触的话,我相信你还是会感到困惑,所以我们首先简单的给个例子。
//异步加载图片
function loadImg(url){
return new Promise(function(resolve,reject){
const image =new Image();
image.src=url;
image.onload=function(){
resolve(image);
}
image.onerror=function(){
reject(new Error('Could not load image at'+url));
}
});
}
//加载图片
loadImg("img/233.jpeg").then(function(image){
document.getElementsByTagName("body")[0].append(image);
}).catch(function(value){
console.log(value);
})
上面的例子还是很简单的,相信读者很容易就会猜到这是一个异步加载图片并显示的代码,当然你也会困惑,什么是resolve/reject,还有then和catch这样的方法,接下来,我会一一解释。
1.Promise
通过上面的例子,我相信读者对于Promise会有一个笼统的了解,promise的英文是“承诺”,就像我们上面的例子一样,promise封装了一个具有异步操作的函数,然后当收到执行结果时再根据状态调用对应的函数执行。
对象刚开始创建时的初始状态是pending(等待),当对象fulfill时会变为fulfilled,当对象reject(失败)时会变为reject的。而且上面所说的两个变化都是单方向而又不可逆的。
相信读者可以很快猜到,当异步的执行完成后,我们便可以通过resolve函数使状态变为fulfilled时,对象执行失败时便可以通过reject函数使状态变为rejected。而这两个函数需要作为参数传入Promise的构造函数,同时这两个参数是JavaScript引擎提供的,你只需要在函数中声明一下就可以使用了。值得一提的是,你也可以通过诸如throw new Error()的方式来改变状态,但是这需要你需要显示的声明catch函数,这在后面我们会细说。。。
你可能会感觉有一点奇怪,因为浏览器本身便会监听图片加载的状态,然后在加载完成时执行对应onload()函数,我们所做的则是在其上再进行一层封装。类似的还有对于XMLHttpRequest请求的封装,这样做其实是将回调函数剥离出来,可以更好的进行模块化复用。也正是因为这样,Promise设计的初衷便是解决“地狱回调”问题。
当然,上面的你暂时看不懂也没有关系,你现在只需要了解Promise的构造方法,学会创建一个Promise对象就可以了。
因为Promise对象封装了一个函数,所以你在创建一个新的Promise对象时需要传入一个函数,类似如下的写法:
//声明promise 函数
function promise(reslove,reject){
var time = Math.random()*2;
if(time <1){
reslove(time);
}
else{
reject(time);
}
}
//创建Promise对象
var p = new Promise(promise);
注意,这种写法在你创建Promise对象时便已经开始执行,所以如果你需要模块化的处理和复用,你可以采用下面的方式:
//函数返回Promise对象
function getPromise() {
return new Promise(function(reslove, reject) {
var time = Math.random() * 2;
if(time < 1) {
reslove(time);
} else {
reject(time);
}
})
}
//创建一个Promise对象
var p= getPromise();
2.then
Promise对象的实例方法之一便是then(),类似的,then()方法也返回一个新的Promise对象,并且可以接受两个函数参数。第一个参数会添加到fulfill时调用的数组中,第二个参数添加到reject时调用的数组中。并且,当promise状态fulfill时,会把resolve(value)中的value传个调用的函数中,例如首例中我们创建 Image对象。同理,当promise状态变为rejected时,会把reject(reason)中的reason值传调给调用的函数。
还是上面我们创建Promise对象的例子,我们再添加上then()函数。
//函数返回Promise对象
function getPromise() {
return new Promise(function(reslove, reject) {
var time = Math.random() * 2;
if(time < 1) {
reslove(time);
} else {
reject(time);
}
})
}
//创建Promise对象
var p= getPromise();
p.then(function(value){
console.log("fulfill time = "+value);
},function(reason){
console.log("reject time = "+reason);
})
很简单对吧!
还记得前面我说的吗?then()方法返回的是一个新的Promise对象,这意味着你可以用类似如下的方式链式调用:
new Promise(…).then(…).then(…);
//函数返回Promise对象
function getPromise() {
return new Promise(function(reslove, reject) {
var time = Math.random() * 2;
if(time < 1) {
reslove(time);
} else {
reject(time);
}
})
}
//创建Promise对象
var p= getPromise();
p.then(function(value){
console.log("fulfill time = "+value);
if(value<0.5){
throw new Error("this too small");
}
},function(reason){
console.log("reject time = "+reason);
if(reason>1.5){
throw new Error("this too big");
}
}).then(function(){
console.log("fulfill second time");
},function(value){
console.log(value);
})
如果你仔细思考并自己尝试,我认为你应该可以得出这样的结论,前面的Promise对象的状态是不会影响后面的Promise对象的状态的。但是,需要注意的是,如果前面的Promise对象的状态没有被正确的接受,比如,你讲上面的代码中第一个then()函数的两个函数参数全部都改为undefined的话,状态会继续传递下去,当然参数value和reason也是一样会被传下去。。。
3.catch
上面我们说过,状态可能会没有被正确接受,比如说:
new Promise(...).then(undefined,undefined);
如果上面的你都看懂了,那么接下来你会很快掌握catch,这也是Promise对象的实例方法。
then(undefined,onRejected(){
//to do something
})
<=>
catch(onRejected(){
//to do something
})
读到这,如果你都尝试过来,那么便可以说对Promise有了基础的了解,如果感兴趣的话,可以接着看下去,里面有Promise的静态方法。
4.resolve 和 reject
这是别的创建Promise对象的方式
Promise.resolve(5);
new Promise(function(resolve){
resolve(5);
})
你很自然的想到,上面的方式得到的其实是fulfilled状态的对象。类似的对比,你可以创建rejected状态的对象。
Promise.reject(5);
5.all 和 race
对于all(),该静态方法接受一个Promise对象数组作为参数,并返回一个新的Promise对象。
只有当所有的对象都变为fulfilled时,新的对象才会变为fulfilled,可以想象的是,所有Promise对象的value依次添加组成一个新的数组作为新对象的resolve的value。
相对的,只要有一个对象变为rejected,新的对象就会变为rejected状态,并且将该对象的reason作为新对象reject的reason。
下面我们看两段代码
function timeout(time) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(time);
}, time);
});
}
console.time('promise')
Promise.all([
timeout(10),
timeout(60),
timeout(100)
]).then(function (values) {
console.log(values); [10, 60, 100]
console.timeEnd('promise'); // 107ms
});
function timeout(time) {
return new Promise(function (resolve,reject) {
setTimeout(function () {
reject(time);
}, time);
});
}
console.time('promise')
Promise.all([
timeout(10),
timeout(60),
timeout(100)
]).then(undefined,function (values) {
console.log(values); [10, 60, 100]
console.timeEnd('promise'); // 32ms
});
我想要强调的两点是:
1.所有的Promise对象是同时开始执行的。
2.当新的Promise对象的状态已经决定了后,后面的Promise对象依旧继续执行,读者可以在reject(time)前加上console(time)来观察效果。
同时需要注意的是,如果传入的数组中有非Promise对象,在fulfill时会被一同传入resolve的value。
对比地去理解race(),你会发现也很简单。同样的是接受Promise数组作为传入参数,不同的是race的含义是竞争,最早执行完的Promise对象将决定新的Promise对象的状态。
有趣的是,如果传入的数组中也有非Promise对象,则是会直接以该值为value执行resolve。
以上便是对于Promise一个非常简单的介绍了,希望各位读后都能有所收获。。。