Promise讲解
15年6月份,ES2015正式发布,其中Promise被列为正式规范,而且作为ES6中最重要的特性之一,我们有必要掌握并理解透彻。本文将会讲解Promise的基本概念与使用方法。
什么是Promise
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和强大。
其实,Promise是一个对象,它可以获取异步操作的数据。
Promise有两个特点:
1。对象的状态不受外界干扰,Promise对象代表一个异步操作,他有三种状态,pending(等待,进行中),resolved(已完成),reject(已失败)。
只有异步操作的结果能决定它的状态,而且一旦决定将无法改变,这也是这是Promise名字的由来—–承诺。
2。Promise的状态只能从pending变成resolved或从pending变成rejected
一旦改变就不可更改。
有了Promise对象,我们就可以吧异步操作以同步操作的流程表达出来了,避免了层层嵌套的回调函数,此外,Promise对象提供了统一的接口,使得控制异步操作更加容易。
基本用法
首先,啥也不说,先打印一下Promise看看吧:
这么一看就明白很多了,promise其实就是一个构造函数,他里面又很多方法,例如all(),race(),reject(),resolve(),它的原型中还有then()和catch()
创建一个Promise实例也很简单,他接受一个函数作为参数,这个函数有接受两个参数,分别为resolve和reject这两个也是函数,分别是在异步操作成功和失败时调用的:
var p = new Promise(function(resolve,reject){
if(异步操作成功){
resolve();
}else{
reject():
}
})
以延时操作为例:
var p1 = new Promise(function(resolve,reject){
setTimeOut(function(){
console.log("执行完成");
resolve("延时执行完成");
},1000)
})
console.log("我是外部操作")
//我是外部操作
//执行完成
上面的代码中,我们执行了一个异步操作,也就是setTimeout,先执行外部操作,然后1秒后,输出“执行完成”,并调用resolve()方法 ,我们能发现,在上面的代码中我们只是new 了一个对象 并没有去调用他,但是函数已经执行了,这就是我们要注意的一个问题。所以我们在用Promise时都是把它包在一个函数中,在需要的时候去调用这个函数。
function run(){
var p1 = new Promise(function(resolve,reject){
setTimeOut(function(){
console.log("执行完成");
resolve("延时执行完成");
},1000)
})
return p1;
}
run();
我们包装好的函数会返回一个Promise的对象。也就是说执行这个函数能得到一个Promise对象,而前面我们也看到了,Promise对象有then和catch方法。
接下来,我么就可以去处理异步操作完成之后的事情了。
run().then(function(data){
console.log(data);
})
then接受一个参数,是函数,并且会拿到我们调用resolve中时传入的参数,运行上面的代码后,会在输出 “执行完成”后紧接着输出“延时执行完成”。
其实这是我们就能发现了,then里面的函数就和我们平时用的回调函数是一个意思,简单的讲就是把原来的回调函数写法分离出来,能使用链式调用的方法来执行回调函数。
那么这时也许有人就要问了,既然效果是一样的,为什么还要用Promise呢。
链式操作
其实Promise的主要用处是来处理多重嵌套的回调函数。
以前的写法遇到回调函数也是一个异步操作时,会特别麻烦,我们要在这个回调函数(callback)执行完之后也要有相应的回调函数,又要定义一个callback2 以此类推。
而使用Promise,我么可以直接在then后面接着写多层嵌套,使用链式的写法来进行回调操作。
例:我定义了三个Promise
function run (){
var p1 = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("第一次函数执行");
resolve("第一次调用成功!");
},1000)
})
return p1;
}
function run2 (){
var p2 = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("第二次函数执行");
resolve("第二次调用成功!");
},2000)
})
return p2;
}
function run3 (){
var p3 = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("第三次函数执行");
resolve("第三次调用成功!");
},500)
})
return p3;
}
现在我要一次去执行他们,我想要在run()执行完之后执行run2 ,然后再执行run3:
run().then(function(data){
console.log(data);
return run2();
}).then(function(data){
console.log(data);
return run3();
}).then(function(data){
console.log(data);
})
这样就能很轻松的使用多层嵌套。
all方法
还是以上面的三个Promise为例:
现在我想要在这三个方法都只需完之后再去执行某些操作,这时就需要用到all方法了。
all方法接受一个Promise的数组
Promise.all([run(),run1(),run2()]).then(function(data){
console.log(data);
});
//第三次函数执行
//第一次函数执行
//第二次函数执行
//[“第一次调用成功”,“第二次调用成功”,“第三次调用成功”]
此时的三个异步操作,并行执行,但是直到三个操作都完成后才会执行,最后的“console.log(data)”这里的data包含了三次异步操作返回的结果。
这个方法可以用到游戏类页面上,直到所有素材都加载完之后,进行页面的初始化。
race方法
race方法与all方法相反,all方法是直到所有异步操作都返回时,执行后续操作,
而race方法则是看谁先执行完,就直接操作后续操作。
还是以上面三个Promise为例:
Promise.race([run(),run2(),run3()]).then(function(data){
console.log(data);
})
//第三次函数执行
//第三次调用成功
//第一次函数执行
//第二次函数执行
在run3完成后就会执行console.log(data),当然这时的data只有run()返回的结果。但是此时的run和run3不会停止,还是会执行。
这个方法的使用场景还是很多的。例如图片加载:
function loadImg(){
var p = new Promise(function(resolve,reject){
var img = new Image();
img.src = 'sad';
img.onload = function(){
resolve("加载完成"); //当执行完这个异步函数时返回成功
}
})
}
function timeOut(){
var p = Promise(resolve,reject){
setTimeout(function(){
reject('图片加载失败');//当执行完这个函数时返回失败
},1000)
}
}
Promise.race([loadImg(),tiemOut()]).then(function(data){
console.log(data);//当返回为成功时执行这条语句
},function(err){
console.log(err);//当返回为失败时执行这条语句
})
//图片加载失败
结果是会在1秒之后输出图片加载失败,因为我们给的是一个无效的src,肯定无法成功请求到,timeOut是一个延时3秒的异步操作,把这两个异步操作放到race方法中,他们会竞赛,如果三秒内图片没有请求成功,那么timeOut就会跑赢了,会返回他的执行完之后的结果,也就是reject(”图片加载失败”);
上面我们的例子都是使用setTimeout这个方法来实现异步操作,但是我相信我们平常用的最多的肯定是ajax的异步操作,promise的用法在ajax中也是一样的。
//创建一个Promise实例,获取数据。并把数据传递给处理函数resolve和reject。需要注意的是Promise在声明的时候就执行了。
var getUserInfo=new Promise(function(resolve,reject){
$.ajax({
type:"get",
url:"index.aspx",
success:function(data){
if(data.Status=="1"){
resolve(data.ResultJson)//在异步操作成功时调用
}else{
reject(data.ErrMsg);//在异步操作失败时调用
}
}
});
})
//另一个ajax Promise对象,
var getDataList=new Promise(function(resolve,reject){
$.ajax({
type:"get",
url:"index.aspx",
success:function(data){
if(data.Status=="1"){
resolve(data.ResultJson)//在异步操作成功时调用
}else{
reject(data.ErrMsg);//在异步操作失败时调用
}
}
});
})
//Promise的方法then,catch方法
getUserInfo.then(function(ResultJson){
//通过拿到的数据渲染页面
}).catch(function(ErrMsg){
//获取数据失败时的处理逻辑
})
//Promise的all方法,等数组中的所有promise对象都完成执行
Promise.all([getUserInfo,getDataList]).then(function([ResultJson1,ResultJson2]){
//这里写等这两个ajax都成功返回数据才执行的业务逻辑
})
好了,本次的Promise的讲解就先到这了,希望能帮到一些小伙伴哦。