Promise是异步编程的一种解决方案,
所谓Promise,就是一个对象,用来传递异步操作的消息。说的通俗点 promise是一个承诺,承诺过一段时间就会给你一个结果
Promise 对象有两个特点:
promise对象状态不受外界影响。它有三种状态:Pending(进行中),Resolved(已完成,又称fulfiled),Rejected(失败) 这三种状态只有异步操作的结果可以决定单曲是那种状态,其他任何操作都无法改变当前状态。Promise状态一旦改变就不会再变,任何时候都可以得到这个结果,状态不可以逆,只能由 Pending变成Resolved或者由Pending变成Rejected平时大家经常写这样的代码:
{//es5 回调写法 let ajax = function(callback){ console.log('执行') setTimeout(function(){ callback&&callback.apply() },1000) } ajax(function(){ console.log('执行2') }) }类似这种一两层的回调函数还是可以忍受的,但是如果加入多次发生你的代码就会变成这个熊样。
fun1(function(){ fun2(function(){ fun3(function( fun4(funciton(){ fun5(function(){ // - - }); }); )); }); }); 这就是所谓的回调地狱,代码层层嵌套,环环相扣,很明显,逻辑稍微复杂一些,这样的程序就会变得难以维护。 这时候我们的promise就应运而生了ES6规定,Promise对象是一个构造函数,用来生成Promise实例。它自身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等方法。
先创造一个Promise对象:
var promise = new Promise (function(resolve,reject){ //做一些异步操作 if(/*异步操作成功*/){ resolve(value) }else{ reject(err) } })Promise构造函数中接受一个函数作为参数,该函数的两个参数分别为reslove和reject。
reslove:异步操作执行成功后的回调函数reject:异步操作执行失败后的回调函数下面看这段代码:
var promise = new Promise(function(resolve,reject){ // some code var data = '数据' setTimeout(function(){ console.log('执行完成') resolve(data) },5000) });上面的代码中,我们执行了一个异步操作,也就是setTimeout,5秒后,输出“执行完成”,并且调用resolve方法。
运行代码,会在5秒后输出“执行完成”。但是 上面我们知识new了一个对象并没有去调用它。所以我们用promise时候一般会包在一个函数中,在需要的时候去调用该函数。下面看下调用的api。
function ajax(){ var promise = new Promise(function(resolve,reject){ // some code var data = '数据' setTimeout(function(){ resolve(data) },5000) }); return promise; } ajax()上面代码中包裹好了promise 直接运行这个函数的话会返回一个promise对象。
现在使用promise带的 方法去实现调用
function ajax(){ //创一个promise方法 var promise = new Promise(function(resolve,reject){ // some code var data = '数据' setTimeout(function(){ resolve(data) },5000) }); return promise; } ajax().then(value=>{//promise调用方式 console.log(value) })then里面的函数就跟我们平时的回调函数一个意思,能够在ajax()这个异步任务执行完成之后被执行。这就是Promise的作用了,简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
Promise 的优势在于,可以在then中继续写promise对象并返回。然后继续调用then来进行回调操作。。。
function runAsync1 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ console.log('异步数据完成1') resolve('数据1') },2000) }) } function runAsync2 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ console.log('异步数据完成2') resolve('数据2') },2000) }) } function runAsync3 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ console.log('异步数据完成3') resolve('数据3') },2000) }) } //链式调用 runAsync1().then(data=>{ console.log(data) return runAsync2() }).then(data=>{ console.log(data) return runAsync3() }).then(data=>console.log(data))运行后发现输出为:
异步数据完成1 数据1 异步数据完成2 数据2 异步数据完成3 数据3在then方法中,也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
到这里,你应该对“Promise是什么玩意”有了最基本的了解。前面的小例子中都是只有成功状态没有失败状态,
reject的作用就是吧Promise的状态设置为rejected,这样我们就能捕获到 然后执行失败的回调。
看例子:
var runAntys= function(){ return new Promise(function(resolve,reject){ var num = parseInt(Math.random()*2) console.log(num) if(num<1){//async resolve() }else{ reject() } }) } ceshi() .then(function(){ console.log('成功') },function(err){ console.log('err') })测试函数用来获取一个异步的的数字,这个随机数在0-2之间,小于1成功的话会走resolve()如果大于1的话我们认为它是失败 改变reject的状态
then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。
catch其实它和then的第二个参数一样,用来指定reject的回调。
p.then((data) => { console.log('resolved',data); }).catch((err) => { console.log('rejected',err); });效果和then第二个参数一样,不过他的另一个作用,在执行resolve的回调时候抛出异常了,并不会报错卡死js,而是会进入catch方法中。
这个与try/catch语句有相同的功能。
谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象。
let Promise1 = new Promise(function(resolve, reject){}) let Promise2 = new Promise(function(resolve, reject){}) let Promise3 = new Promise(function(resolve, reject){}) let p = Promise.all([Promise1, Promise2, Promise3]) p.then(funciton(){ // 三个都成功则成功 }, function(){ // 只要有失败,则失败 })all 的应用场景还是很多的,比如 素材比较多的网页。打开后预先加载需要各种资源的图片 flash或者静态文件,我们用all等他们全部加载完成后,再让页面进行初始化。
举例 用settimeout去做响应时间看效果
{//打飞机比赛 function runAsync1 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ resolve('老佳同学') },1000) }) } function runAsync2 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ resolve('小樊凡同学') },2000) }) } function runAsync3 (){ return new Promise ((resolve,reject)=>{ setTimeout( a =>{ resolve('王老五同学') },3000) }) } let race = Promise.race([runAsync1(),runAsync2(),runAsync3()]) race.then((data)=>{//race代表 竞赛关系 第一名胜出 console.log('第一名是:'+data) }).finally(() => console.log('每个同学都很厉害 都没超过三秒')) //ES9 Promise 新增api 无论 promise 的执行成功或失败都会去执行 }这三个异步操作同样是并行执行的。结果你应该可以猜到,1秒后runAsync1已经完了,此时then里面的就执行最先完成的异步。
我们还可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。
{ function getImgSrc(){ return new Promise((resolve,reject)=>{ var img = new Image(); img.onload = function(){ resolve(img) } img.src = '错误的地址' //img.src = 'http://pic.58pic.com/58pic/15/68/59/71X58PICNjx_1024.jpg' }) } function timeOut (){ return new Promise ((resolve,reject)=>{ setTimeout(function(){ reject('图片获取失败') },5000) }) } let getimg = Promise.race([getImgSrc(),timeOut()]) getimg.then(data=>{ console.log(data) }).catch(data=>{ console.log(data) }) }上面代码中做了一个 getimgsrc会异步的获取图片,假设把scr设置为错误的地址,肯定是拿不到图片数据的,timout函数是一个延时函数。我们吧这两个函数放入 race中,让他们赛跑。如果五秒钟内图片请求成功了。那么进入 then 正常加载。如果超过五秒还没有返回成功。那么进入catch 并且抛出 图片获取失败。
上面例子中也有使用过
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
fetch('file.json') .then(data => data.json()) .catch(error => console.error(error)) .finally(() => console.log('finished'))