教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

淺析Promise用法

更新時(shí)間:2018年10月19日16時(shí)46分 來源:傳智播客 瀏覽次數(shù):

所謂Promise,簡單說就是一個(gè)容器,里面保存著某個(gè)未來才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果。從語法上說,Promise 是一個(gè)對象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進(jìn)行處理。

Promise對象有以下兩個(gè)特點(diǎn)。
(1)對象的狀態(tài)不受外界影響。Promise對象代表一個(gè)異步操作,有三種狀態(tài):Pending(進(jìn)行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個(gè)狀態(tài)。這也是Promise這個(gè)名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態(tài)改變,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果。Promise對象的狀態(tài)改變,只有兩種可能:從Pending變?yōu)镽esolved和從Pending變?yōu)镽ejected。只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會(huì)再變了,會(huì)一直保持這個(gè)結(jié)果。就算改變已經(jīng)發(fā)生了,你再對Promise對象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果。這與事件(Event)完全不同,事件的特點(diǎn)是,如果你錯(cuò)過了它,再去監(jiān)聽,是得不到結(jié)果的。
有了Promise對象,就可以將異步操作以同步操作的流程表達(dá)出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易。

要理解Promise要知道沒有Promise的回調(diào)地獄:
一般我們要在一個(gè)函數(shù)執(zhí)行完之后執(zhí)行另一個(gè)函數(shù)我們稱之為callback‘回調(diào)’,簡單的寫一下:

setTimeout(function(){
  left(function(){
    setTimeout(function(){
       left(function(){
         setTimeout(function(){
           left();
         },2000);
       });
    }, 2000);
  });
}, 2000);
以上代碼就是傳說中的回調(diào)地獄,如果有多層業(yè)務(wù)邏輯嵌套的話,不僅會(huì)使代碼閱讀困難,而且后面維護(hù)起來也是難點(diǎn)。
之后在ES6,Promise就應(yīng)運(yùn)而生。

Promise語法與then的用法:
var promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
resolve(value)是在Promise在已經(jīng)異步完成成功(Resolved)之后執(zhí)行的
reject(value)是在Promise在異步失敗之后(Rejected)執(zhí)行。
當(dāng)然,也可以用then來指定:then(resolve,reject)
或者:then(resolve),catch(reject)

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});
//等價(jià)于:
promise.then(function(){
  //success
}).catch(function(){
  //failure
})
范例展示:寫一個(gè)img圖片加載示例點(diǎn)擊

Tips
連續(xù)調(diào)用回調(diào):
以剛開始的回調(diào)地獄為例子:
setTimeout(function(){
  left(function(){
    setTimeout(function(){
       left(function(){
         setTimeout(function(){
           left();
         },2000);
       });
    }, 2000);
  });
}, 2000);
//我們給left函數(shù)內(nèi)容換成console.log(11);
var p = new Promise((resolve,reject)=>{
    setTimeout( resolve , 2000 )
  })
  .then( ()=>setTimeout( null, 2000 ) )
  .then( ()=>setTimeout(function(){
    console.log(11)
  },2000) )
//這樣在6秒鐘之后會(huì)打出11
范例點(diǎn)擊
總結(jié):

可以采用連續(xù)的then鏈?zhǔn)讲僮鱽韺懟卣{(diào)(這是因?yàn)榉祷刂狄恢笔切碌腜romise實(shí)例)。
以上例子可以看出來只要在第一個(gè)promise回調(diào)中添加resolve,之后的連續(xù)then就會(huì)默認(rèn)執(zhí)行。
可以在then中return出數(shù)據(jù),并且這個(gè)數(shù)據(jù)會(huì)以參數(shù)的形式傳入下一個(gè)then。
var p = new Promise(function(resolve,reject){
      var a=1
      resolve(a);  
      
  }).then(function(data){
      console.log(data)
      return ++data;
  }).then( function(data){
      console.log(data)
  } )
//打印出來的結(jié)果依次是: 1  2 。
接下來介紹一下catch()
catch是用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。(建議不要在then的第二個(gè)參數(shù)寫rejected狀態(tài),總是使用catch)
catch()使回調(diào)報(bào)錯(cuò)時(shí)不會(huì)卡死js而是會(huì)繼續(xù)往下執(zhí)行。點(diǎn)擊范例
Promise 對象的錯(cuò)誤具有“冒泡”性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說,錯(cuò)誤總是會(huì)被下一個(gè)catch語句捕獲。
如:
getJSON('/post/1.json').then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // 處理前面三個(gè)Promise產(chǎn)生的錯(cuò)誤
});
用一段小代碼來理解catch()

var p = new Promise((resolve,reject)=> {
    n
  } ).then(()=>console.log('運(yùn)行成功'))
  .catch( ()=>{a;console.log('報(bào)錯(cuò)');} )//這里我們沒有定義a的值會(huì)報(bào)錯(cuò)
  .catch( ()=> console.log('報(bào)錯(cuò)2') )
  .then( ()=>console.log('報(bào)錯(cuò)后的回調(diào)') )
//運(yùn)行結(jié)果是:'報(bào)錯(cuò)2'  '報(bào)錯(cuò)后的回調(diào)'
首先n沒有定義,所以第一層出錯(cuò)。下一個(gè)then的‘運(yùn)行成功’不會(huì)被打出來。而是會(huì)被下一個(gè)catch捕獲,第一個(gè)catch沒有定義a,所以報(bào)錯(cuò),console.log('報(bào)錯(cuò)')沒辦法打出來,又被下一個(gè)catch捕獲: 第二個(gè)catch沒有問題:打出‘報(bào)錯(cuò)2’。運(yùn)行成功傳給下一個(gè)then,打出'報(bào)錯(cuò)后的回調(diào)'。

這里要注意,不管是then或者catch返回的都是一個(gè)新的Promise實(shí)例!而每個(gè)Primise實(shí)例都有最原始的Pending(進(jìn)行中)到Resolve(已完成),或者Pending(進(jìn)行中)到Reject(已失?。┑倪^程。
Promise.all()
Promise.all方法用于將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。

如:

var p = Promise.all([p1, p2, p3]);
all()接受數(shù)組作為參數(shù)。p1,p2,p3都是Promise的實(shí)例對象,p要變成Resolved狀態(tài)需要p1,p2,p3狀態(tài)都是Resolved,如果p1,p2,p3至少有一個(gè)狀態(tài)是Rejected,p的狀態(tài)就變成Rejected(個(gè)人感覺很想&&符號(hào)鏈接)

Promise.race();
var p = new Promise( [p1,p2,p3] )
上面代碼中,只要p1、p2、p3之中有一個(gè)實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個(gè)率先改變的 Promise 實(shí)例的返回值,就傳遞給p的回調(diào)函數(shù)。(感覺就是||符號(hào)操作~~~)

Promise resolve():
有時(shí)需要將現(xiàn)有對象轉(zhuǎn)為Promise對象,Promise.resolve方法就起到這個(gè)作用。
Promise.resolve等價(jià)于下面的寫法。

Promise.resolve('foo')
// 等價(jià)于
new Promise(resolve => resolve('foo'))
Promise reject()
Promise.reject(reason)方法也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected。

Promise.reject('foo')
// 等價(jià)于
new Promise(reject => reject('foo'))
注意,Promise.reject()方法的參數(shù),會(huì)原封不動(dòng)地作為reject的理由,變成后續(xù)方法的參數(shù)。這一點(diǎn)與Promise.resolve方法不一致。

作者:傳智播客前端與移動(dòng)開發(fā)培訓(xùn)學(xué)院
首發(fā):http://web.itcast.cn/

0 分享到:
和我們在線交談!