浏览器的控制台真是个好东西,啥都能干:
这就是Promise,能看出来啥?
1、是个构造函数,可以new实例。
2、自身有一些方法:all、race、reject、resolve...
3、原型上有catch、then...
玩玩吧
Promise的构造函数接收一个参数,是函数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。Promise是有状态的:resolve时,Promise的状态为fullfiled,reject时,Promise的状态为rejected。
<script> var p = new Promise(function(resolve, reject){ //模拟异步操作 setTimeout(function(){ console.log('执行完成'); resolve('成功之后返回的数据'); }, 2000); }); </script>
在上面的代码中,我们执行了一个异步操作,也就是setTimeout,2秒后,输出“执行完成”,并且调用resolve方法。
有没有感觉怪怪的,是的,我只是new了一个Promise对象,传进去的函数就已经执行了。为了方法执行可控且合理,我们把它放在一个方法里,调用的时候才执行:
<script> function runAsync(){ var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ console.log('执行完成'); resolve('执行成功之后的数据'); }, 2000); }); return p; } runAsync(); </script>
我们说了Promise是一个包含了执行状态(成功或失败)的对象,所以我们在上边的方法中返回了这个对象,以便我们接下来对相应结果进行处理。怎么处理呢?这里就要用到then,catch方法:
<script> function runAsync(){ var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ console.log('执行完成'); resolve('执行成功之后的数据'); }, 2000); }); return p; } runAsync().then(function(data){ console.log(data); //后面可以用传过来的数据做些其他操作 //...... }); </script>
在runAsync()的返回上直接调用then方法,then接收一个参数,是函数,并且会拿到我们在runAsync中调用resolve时传的的参数。运行这段代码,会在2秒后输出“执行完成”,紧接着输出“执行成功之后的数据”。
和普通回调函数的区别
我们平时用的回调函数,其执行过程是,主函数执行(异步请求成功)之后,回调函数就立即执行了。看一下
<script> function runAsync(callback){ setTimeout(function(){ console.log('执行完成'); callback('回调函数执行'); }, 2000); } runAsync(function(data){ console.log(data); }); </script>
所以普通回调的特点就是,异步请求成功,就会立即执行。而Promise对象的不同,就是在异步请求之后,把请求之后的状态(成功或失败)和数据赋给了返回的Promise对象。在我们需要对异步请求的结果进行处理的时候,我们只要单独的对这个Promise对象进行操作就行了。
第二个区别就是,普通的回调函数如果也是一个异步请求,就会出现异步请求嵌套,如果层级比较多就会显得很臃肿。虽然我们可以采用命名函数进行优化,但是涉及到闭包和传参,总是很复杂。Promise就解决了这个问题,它能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数,就是Promise.then().then().then().......
来点干货,实现一下红绿灯的效果:
<style type="text/css"> .light{list-style: none;} .light li{width: 20px;height: 20px;border: 1px solid #F1F1F1;border-radius: 50%;float: left;margin-right: 2px;} .light li:last-child{position: relative;top: 20px;left: -36px;} .light-red{background: #F00;} .light-blue{background: #0F0;} .light-yellow{background: #FF0;} .light-default{background: #FFF;} </style> <ul class="light"> <li class="light-red light-default"></li> <li class="light-yellow light-default"></li> <li class="light-blue light-default"></li> </ul> <script type="text/javascript"> //Promise对象,resolve返回数据 function turnLight(n){ var p = new Promise(function(resolve,reject){ setTimeout(function(){ resolve(n); },300); }); return p; } function runLight(){ //执行 turnLight(0) .then(function(n){ toggleClass(n); return turnLight(n+1); }) .then(function(n){ toggleClass(n); return turnLight(n+1); }) .then(function(n){ toggleClass(n); }); } clearInterval(timer); var timer = setInterval(runLight,900); function toggleClass(n){ $(".light li").eq(n).removeClass("light-default").siblings().addClass("light-default"); } </script>
Promise对象还有一些其他的方法:
reject方法
resove方法是在异步请求成功之后返回数据的,reject方法一般是在请求失败之后处理数据的。
<script> function getNumber(){ var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ var num = Math.ceil(Math.random()*10); //生成1-10的随机数 if(num<=5){ resolve(num); } else{ reject('数字太大了'); } }, 2000); }); return p; } getNumber() .then(function(data){ console.log('resolved'); console.log(data); },function(reason, data){ console.log('rejected'); console.log(reason); }); </script>
这里模拟了异步请求的成功和失败。当随机数小于等于5,认为是成功,大于5认为是失败的。then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。
catch方法
catch方法就是把then方法的第二个回调单独列出来,对应reject
<script> function getNumber(){ var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ var num = Math.ceil(Math.random()*10); //生成1-10的随机数 if(num<=5){ resolve(num); } else{ reject('数字太大了'); } }, 2000); }); return p; } getNumber() .then(function(data){ console.log('resolved'); console.log(data);
console.log(dataaa); }) .catch(function(reason, data){ console.log('rejected'); console.log(reason); }); </script>
在then里多写一个输出,打印dataaa,一般的方法到这里肯定会报错,但是在这里,不会报错,而是继续进入catch方法执行,并且在catch里输出报错信息。这与try/catch语句有相同的功能。
all方法
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
<script> function getNumber(){ var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ var num = Math.ceil(Math.random()*10); //生成1-10的随机数 resolve(num); }, 2000); }); return p; } Promise.all([getNumber(), getNumber(), getNumber()]).then(function(results){ console.log(results);//[7, 5, 8] }); </script>
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。
race方法
和all方法相对应,race方法则是有一个操作执行完,就执行回调
<script> function getNumber(n){ var p = new Promise(function(resolve, reject){ //做一些异步操作 setTimeout(function(){ var num = Math.ceil(Math.random()*10); //生成1-10的随机数 console.log("第"+n+"个执行完,输出"+num); resolve(num); }, 2000); }); return p; } Promise.race([getNumber(1), getNumber(2), getNumber(3)]).then(function(results){ console.log(results); }); //第1个执行完,输出3 //3 //第2个执行完,输出1 //第3个执行完,输出4 </script>
然而第一个执行完之后,后两个并没有停止,而是继续执行,执行了打印。只是没再执行resolve。race方法的作用可以进行加载超时的控制:
<script> //请求某个图片资源 function requestImg(){ var p = new Promise(function(resolve, reject){ var img = new Image(); img.onload = function(){ resolve(img); } img.src = 'xxxxxx'; }); return p; } //延时函数,用于给请求计时 function timeout(){ var p = new Promise(function(resolve, reject){ setTimeout(function(){ reject('图片请求超时'); }, 5000); }); return p; } Promise .race([requestImg(), timeout()]) .then(function(results){ console.log(results); }) .catch(function(reason){ console.log(reason); }); </script>
代码很简单,试一下吧~~