Promise對象

promise對象用來管理異步操做(將來某個時刻發生的事),由Promise構造函數生成,有一些內置的api,經過這些api能以同步的代碼書寫方式來表示異步操做,避免不斷嵌套的回調函數的書寫方式。能夠把Promise對象看作一個容器,裏面保存着異步操做的結果。
promise對象用三種狀態來表示異步操做的狀態:pending(進行中),resolved(已完成),rejected(失敗了,拋出錯誤了),狀態只能由pending變成resolved或由pending變成rejected,經過promise對象的api能夠(提早)指定當變到resolved或rejected狀態時的回調函數,而什麼狀況下是resolved狀態,什麼狀況下是rejected狀態,能夠由咱們本身決定。ajax

主要api:json

  1.生成一個primise對象(實例):api

var p = new Promise(function(resolve, reject) {
  // 一些異步操做, 獲得操做結果result
  var result = ...
  if (result == 'ok') { // 設置這種狀況下調用resolved的回調函數
    resolve(result)
  } else {
    reject('not ok')
  }
})

  其中的resolve, reject分別表示resolved和rejected時的回調函數,而且能夠傳任意參數。如代碼所示,當執行resolve(result)時便是把promise對象的狀態變成resolved狀態,當執行reject('not ok')時便是把promise對象的狀態變成rejected狀態.數組

  2.Promise.prototype.then()設置resolved和rejected的回調函數promise

p.then(function(val) {
  console.log(val)  // 'ok'
}, function (val) {
  console.log(val) // 'not ok'
})    

  then方法接受兩個函數參數,第一個參數就是resolved的回調函數,第二個參數是rejected的回調函數(第二個參數是可選的)異步

一個簡單的例子:async

var p = new Promise((resolve, reject)=>{
  var result = true
  var v='aaa '
  if(result) {
    resolve(v)
  } else {
    reject(v)
  }
})
p.then( v => console.log(v+'resolve')  // 'aaa resolve'
, v => console.log(v+'reject') )// 'aaa reject'

一個簡單的例子:函數

console.log(0)
var demo = function(){
  return new Promise((resolve, reject) => {
    setTimeout(resolve, 5000, 'aaa ')
  })
}
demo().then( v => console.log(v+'resolve'), v => console.log(v+'reject'))
console.log(1)

// 運行結果
0
1
(5s後) 'aaa resolve'

一個異步加載圖片的簡單例子:post

function loadImgAsync (url) {
  return new Promise( (resolve, reject) => {
    var img = new Image()
    img.onload = ()=> resolve(img)
    img.onerror = () => reject(new Error('not ok'))
    img.src = url
  })
}
var url = 'http://img3.duitang.com/uploads/item/201509/02/20150902131938_yEJVA.jpeg'
loadImgAsync(url).then(() => console.log('ok'), err => console.log(err.message) )

 .then()方法返回的是一個新的Promise實例,因此能夠採用鏈式寫法,.then().then(),前一個then回調函數的return返回值會做爲參數傳到後一個then的回調函數裏,只有當前一個Promise對象狀態發生變化纔會調用下一個then的方法。ui

一個簡單的例子:

p.then( () => {
    console.log('resolve1')  
    return 111
}, () => {
    console.log('reject1')
    throw "empty"
}).then( v =>  console.log(v+' resolve2'), v => console.log(v+' reject2') )

// 若p執行resolved, 則依次 'resolve1', '111 resolve2' 
// 若p執行rejected, 則依次 'reject1', '111 reject2' 
// 若p執行rejected, 同時註釋掉throw "empty"或換成 return new Error(),則會依次 'reject1', ' resolve2'!!!

因而可知只有在程序拋出異常或是Promise對象中手動指定調用reject(),只有這兩張狀況下會執行reject()。

好比: 假設getJson(url)會返回一個Promise對象,用來執行ajax操做,成功時返回結果做爲參數傳給resolve函數,失敗時返回一個Error對象傳給reject函數。

getJSON('/post/1.json')
.then(post => getJSON(post.commentUrl))
.then( comment => console.log(comment), err => console.log(err) )

上例就是先取post信息,取到後再去取comment信息。

3.Promise.prototype.catch((err)=>{})

異步操做拋出錯誤時的回調函數,至關於.then(null, rejectFun),錯誤具備'冒泡'特性,會一直向後傳遞,因此:

p
.then()
.then()
.catch((err)=>{
// 處理前面三個promise產生的錯誤
})

 通常來講,不推薦p.then(okFun, errorFun), 而應該老是使用p.then(okFun).catch(errorFun), 這種寫法更符合同步的寫法。注意Promise對象拋出的異常不會傳遞到外層,全部若是沒有.catch(), 即便添加了try{p.then()} catch(err) {},p發生錯誤時也不會被catch到。

4. Promise.all()

接受一個由Promise實例組成的數組做爲參數,一樣返回一個Promise實例,至關於把多個Promise實例包裝成一個新的Promise實例。如 var p = Promise.all([p1,p2,p3]),只有p1, p2, p3都是resolved,p纔會是resolved,此時p1, p2, p3的返回值組成一個數組,傳給p的回調函數;只要p1, p2, p3有一個rejected,p就是rejected,此時第一個被rejected的實例的返回值傳給p的回調函數。

5. Promise.race([p1, p2,p3])

相似於Promise.all(), 區別是隻要p1, p2, p3中有一個實例率先改變狀態,p的狀態就跟着改變。

6. Promise.resolve()

能夠把非Promise對象轉爲Promise對象,若是已是Promise對象,則原封不動的返回。

var p = Promise.resolve('hello') 即至關於 var p = new Promise( resolve => resolve('hello')),'hello'不是異步操做,狀態會立馬變成resolved,當即執行回調函數;

var p = Promise.resolve( $.ajax('/a.json') ) 即至關於 var p = new Promise( resolve => {vap rsp = $.ajax('/a.json') ;resolve(rsp); )

也能夠 var p = Promise.resolve(); p.then()

7. Promise.reject()

相似Promise.resolve(),var p = Promise.reject('hello') 即至關於 var p = new Promise( (resolve, reject) => reject('hello'))

8. async, await關鍵字

Promise的寫法雖然比起普通回調函數的寫法有不少改進,但一眼看上去,代碼徹底是Promise的api(.then().catch()),操做自己的語義還不是特別明顯,而用async函數的寫法最符合語義,沒有與語義不相干的代碼。

async關鍵字表示該函數內部有異步操做,await 後面是一個Promise對象,執行到await就會先返回,等到觸發的異步操做完成,再接着執行函數體內後面的語句

一個簡單的例子:

var p = () => {return new Promise( resolve => setTimeout(resolve, 5000))}
var fun = async () => {
  console.log(1)
  await p().then(()=>console.log(2))
  console.log(3)
}
fun()
console.log(4)

// 運行結果
//1
//4
//(5s後)2
//3

注意:await 只能運行在一個函數裏,且只能運行在async函數裏,用在普通函數裏會報錯。由於await表示這裏須要等待,await後面的代碼沒法當即執行,把await放在async函數裏面後,執行到await就會跳出這個函數,繼續執行函數外面的代碼,等await返回異步操做結果後,再繼續執行這個async函數內await後面的代碼。另外由於await後面的Promise對象的運行結果有多是rejected,全部最好把await命令放在try...catch代碼塊中。

一個sleep函數,暫停程序:

const sleep = (ms) => {
  return new Promise(resolve => setTimeout(resolve, ms))
};
const demo1 = async() =>{
  console.log(1)
  await sleep(5000).then(() => console.log(2))
  console.log(3)
}
demo1();
// demo1() 或 demo2都可以
const demo2 = async() =>{
  console.log(1)
  await sleep(5000)
  console.log(2)
  console.log(3)
}
demo2();

// 運行結果
// 1
// (5s後)2
// 3
相關文章
相關標籤/搜索