ECMAScript 6.0(es6)已经出来三年多了,相对es5,es6有很多新特性值得我们去探究,其中Promise对象可以说真的是很有趣呢!笑哭.jpg
Promise是什么?他可是对象啊。你能亲手new一个promise,然后让他为你做他能做到的事情,就算做不到,你还能给他加技能,想想就很开心。
js是一门神奇的语言,他成功做到了把函数作为参数。
下面的代码很容易理解的:
var wait=function (time,callback) { setTimeout(function () { callback(); },time) }; wait(300,function () { console.log('等300ms我该做点啥呢?'); //todo })
这里的wait是个函数,它接收两个参数,其中的callback就是一个函数。
这时候你是不是想说,回调用着还蛮带劲的,别别别,当你遇到多重回调你会疯掉的,前方高能,请做好一级战斗准备:
var fun=function (callback1,callback2,callback3) { var data=1; setTimeout(function () { console.log('first:'+data); callback1(callback2,callback3,data) },1000) }; var callback1=function (callback2,callback3,data) { setTimeout(function () { console.log('second:'+data); callback2(callback3,data) },1000) }; var callback2=function (callback3,data) { setTimeout(function () { console.log('third:'+data); callback3(data) },1000) }; var callback3=function (data) { setTimeout(function () { console.log('firth:'+data); },1000) }; fun(callback1,callback2,callback3);
怎么样?惊不惊喜意不意外开不开心?就为了传个数据,就写了四个函数,这特么还能不能愉快的写代码了?
别急,这时候可以有请我们的Promise闪亮登场了。
且看下面一段代码:
var fun = function () { return new Promise(function (resolve, reject) { setTimeout(function () { console.log('first:1'); resolve('1') }, 1000) }) }; fun() .then(function (data) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log('second'+data); resolve(data) }, 1000) }) }) .then(function (data) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log('third'+data); resolve(data) }, 1000) }) }) .then(function (data) { return new Promise(function (resolve, reject) { setTimeout(function () { console.log('firth'+data); resolve(data) }, 1000) }) })
怎么样,这一波链式操作是不是看起来舒服多了?舒服是舒服,就是有点儿不理解。那么我们就来一点点分析。
上面的代码中:fun可是一个正经的函数,它的返回值是一个promise对象。我们看到,promise对象还有一个then属性,这个then是做什么的呢,我们后面会介绍。
promise就是一个代表了异步操作最终完成或者失败的对象,既然promise是个对象,那他自然有自己的构造函数。promise的构造函数接受一个函数参数,这个函数有两个参数,都是由js引擎提供,不需要自己去实现的。
第一个参数resolve(当然你命名成阿猫阿狗都行的)表示将Promise的状态置为fullfiled,reject是将Promise的状态置为rejected。这么说可能有些晦涩难懂,其实吧,他们都是函数,这个函数什么时候执行呢?这取决于你。不过我们约定俗成的做法是,在异步操作执行成功的时候,调用resolve,在异步操作执行失败的时候调用reject。resolve和reject都要结合promise的then属性来使用,then是一个函数,他的两个参数分别对应promise的resolve和reject。
这段代码的执行结果如下图(每隔一秒输出一行):
上面只验证了resolve,我们再来验证下reject:
可能你会问,这并不是异步操作啊?对的,这确实不是异步操作,这只是为了验证promise构造函数里面的resolve和reject与then属性的回调函数对应关系,你甚至可以这么写:
到这里,想必你已经对Promise已经有了大致的了解,没错,promise就是一个普通的拥有着众多属性的对象,那么我们继续看看promise原型上的方法吧(哈?你问什么是原型,那么出门右拐,说不定能找到原型的介绍~)。
1、then
为promise实例提供状态改变时的回调函数。(前面已经介绍了,这里就不继续废话了)
2、catch
我们都知道以下写法:
try{ //somecode }catch(error){ }
普通的catch的作用是用来捕获try里面的代码块执行过程中的异常或错误,如果“somecode”里面有错误,不捕获的话就会影响后续的代码执行,这样就可能对整体代码造成创伤。
和普通catch一样,Promise原型上的catch方法能捕获到resolve里面的异常,举个栗子:
new Promise(function (resolve, reject) { setTimeout(function () { console.log('first:1'); resolve('1') }, 1000) }).then(function(data){ console.log(someVal); }) .catch(function(reason){ console.log('出错了:'); console.log(reason); });
来看下控制台执行结果:
可以看到,Promise的catch与普通的catch方法有着相似的功能。
再来看一个栗子:
new Promise(function (resolve, reject) { setTimeout(function () { reject('1') }, 1000) }).then(function(data){ console.log(someVal); }) .catch(function(data){ console.log(data); });
执行结果如下:
所以说,catch的回调可以当成reject即then的第二个函数来使用。
3、finally
finally的字面意思是最终的,在promise对象上表示这个对象最终执行的方法,就是说,不管是resolve还是reject,最终都会进finally方法。
来看代码:
new Promise(function (resolve, reject) { setTimeout(()=>{resolve(1);},1000) setTimeout(()=>{reject(2);},2000) }) .then(result => {console.log(result)}) .catch(error => {console.log(error)}) .finally(() => {console.log('finally')});
执行结果如下:
在这个栗子中,Promise中的状态为fulfilled,但在执行了resolve方法后,还是输出了‘finally’。
其实:finally()方法返回一个promise对象,在执行then()和catch()后都会执行finally指定的回调函数,避免同样的代码在then和catch中都写一次,什么意思呢?且看:
new Promise(function (resolve, reject) { setTimeout(()=>{resolve(1);},1000) setTimeout(()=>{reject(2);},2000) }) .then(result => {console.log('我是异步操作之后必须要执行的代码')}) .catch(error => {console.log('我是异步操作之后必须要执行的代码')})
输出如下:
其实,针对上述代码,finally就能搞定,你只需要这样写就行:
new Promise(function (resolve, reject) { setTimeout(()=>{resolve(1);},1000) setTimeout(()=>{reject(2);},2000) }) .finally(() => {console.log('我是异步操作之后必须要执行的代码')})
不信你就执行看看啊。
讲完了Promise原型上的方法,接下来我们来讲讲Promise构造函数内的方法。
1、all
Promise.all([new Promise(function (p1, p2) { setTimeout(() => { console.log(1); p1(1) }, 1000) }), new Promise(function (p1, p2) { setTimeout(() => { console.log(2); p1(2) }, 2000) }), new Promise(function (p1, p2) { setTimeout(() => { console.log(3); p1(3) }, 3000) })]).then(function (results) { console.log(results); })
先看看执行结果:
我们来分析一下:all方法接收一个数组作为参数,这个数组的元素全是Promise实例,当这三个Promise的状态都改变时,会进入到all对应的Promise构造函数的then方法,这个then的回调函数参数则是all方法接收的Promise传递的数据封装后的数组。
也就是说,有了all,你可以并行执行多个异步操作,all会把所有异步操作的结果放进一个数组中传给then。这在很多场景下都适用。比如说某个页面,ABC资源的加载的都是异步的,且之间没有依赖关系,我们需要在ABC资源都加载完成后才执行一些神操作,那么这时候all就可以解决这个问题。
2、race
race和all的代码格式差不多,怎么办,我懒得贴代码了,口述吧。
前面我们说all时等所有的异步操作完成后会进入then的回调,这里的race就不一样了,它表示只要有异步操作执行完了,就会就如then回调,且不影响其他的异步操作的执行。
哎,还是贴贴一段代码吧。
看到了吧,这里输出了两个1,第二个1时执行最快的那个Promise传个race的then回调了。那么race有什么用了,它作用大着呢,它可以可以给某个异步操作设置超时时间。比如说:
3、resolve
这时候你是不是懵逼了,怎么构造函数内部还有resolve方法,他不是构造函数的回调的参数么?哈哈,没办法,Promise创始人让它有的,我们没办法阻止的。
resolve方法返回一个以给定值解析后的Promise对象。
也就是说,resolve会返回一个Promise的实例,这个实例的then方法的第一个回调函数能获取到resolve传过去的数据,进而对数据进行操作。
4、reject
与resolve差不多,这里就不细讲了。
呼,总算写完了~~~~~~~~