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