Promise 是異步編程的一種解決方案,其餘的異步編程解決方案還有——回調函數、事件監聽、發佈訂閱,以及ES6新增的Generator 。javascript
http://www.ruanyifeng.com/blo...
http://es6.ruanyifeng.com/#do...html
Pending(進行中)java
Resolved (已完成)node
Rejected (已失敗)es6
對象的狀態不受外界影響。數據庫
一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。編程
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操做成功 */){ resolve(value); } else { reject(error); } });
注意:json
resovle和reject函數都是由js引擎提供的函數不用本身定義。數組
其中 resovle函數的做用是將Promise對象從進行中狀態轉換爲已完成,reject函數將Promise對象從進行中狀態轉換爲已失敗狀態。promise
Promise 實例具備then方法,then方法是定義在原型對象Promise.prototype上的,它的做用是爲 Promise 實例添加狀態改變時的回調函數。
延續上面的代碼:
promise.then(function(res) { console.log(res) }, function(err) { console.log(err) })
then對象接收兩個回調函數做爲參數,第一個回調函數是resovle的回調,第二個回調函數是reject的回調。即,一個用於處理狀態爲已完成的邏輯,另外一個用於處理已失敗的邏輯。
一個Ajax請求的例子:
var getJSON = function(url) { var promise = new Promise(function(resolve, reject){ var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出錯了', error); });
Promise.prototype.catch方法是.then(null, rejection)的別名,用於指定發生錯誤時的回調函數。不是指then方法的別名而是指resovle的回調函數爲null的then方法的別名。
一般推薦使用catch方法來捕獲異常,而不是使用reject的回調函數。
// 不推薦的寫法 promise .then(function(data) { // success }, function(err) { // error }); // 推薦的寫法 promise .then(function(data) { // success }) .catch(function(err) { // error });
使用catch方法來接收錯誤信息的好處是,Promise 對象的錯誤具備「冒泡」性質,會一直向後傳遞,直到被捕獲爲止。因此一個catch能夠捕獲鏈式調用中多個Promise對象拋出的異常。
當有幾個異步操做須要鏈式調用時,promise是支持的。
一個簡單的鏈式調用的例子:
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function funcA(comments) { console.log("Resolved: ", comments); }, function funcB(err){ console.log("Rejected: ", err); });
Promise的then函數,返回的是一個新的Promise對象。若是返回的對象不是Promise對象會是什麼效果呢?
如下是一些測驗代碼:
var promise = new Promise(function(resolve, reject) { let a = '111'; resolve(a); }); promise.then(function(res) { return null; }).then(function(res){ console.log(res); }).catch(function(err) { console.log(err); }) // null
var b = function() { console.log('bb'); } var promise = new Promise(function(resolve, reject) { let a = '111'; resolve(a); }); promise.then(function(res) { return b(); }).then(function(res){ console.log(res); }) // bb // undefined
能夠看出,若是then retrun的不是一個Promise函數時,並不會報錯,也沒有拋出異常,至關於一個沒有執行reject和resovle的Promise對象(我的理解,有失偏頗。望自慎重,擇善從之。)。
在某些時候,咱們須要執行多個異步操做。這些異步操做可能有如下幾種關係:
互不關聯
逐個依賴
部分依賴
結果依賴(指須要得到因此返回的結果進行下一步的操做)
Promise.all方法用於將多個 Promise 實例,包裝成一個新的 Promise 實例。能夠用來解決上面的4關係。
互不關聯最好分開調用,部分依賴、逐個依賴用鏈式調用。
Promise.all接收一個Promise數組做爲參數,即數組中的值都是Promise對象,若是不是就會先調用Promise.resovle方法,將參數轉爲Promise實例。
// 生成一個Promise對象的數組 var promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON('/post/' + id + ".json"); }); Promise.all(promises).then(function (posts) { // ... }).catch(function(reason){ // ... });
只有當全部的Promise對象都返回resovle時纔會執行then方法,只要有一個Pormise對象返回的是reject,就執行catch。
ps: 還有Promise.race()方法,此方法和Promise.all()不一樣之處時,只要傳入的Promise數組中有一個實例率先改變狀態,p的狀態就跟着改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。可用於限制請求的最大反應時間。
const p = Promise.race([ fetch('/resource-that-may-take-a-while'), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); p.then(response => console.log(response)); p.catch(error => console.log(error)); // 若是超過5秒就返回請求超時
這兩個方法返回一個新的Promise實例,resovle方法返回的實例狀態爲resolve,同理reject方法返回reject。
都接收一個參數做爲現有的對象,此參數能夠爲任何值包括null,undefined。
根據接收的參數,兩種方法都會作出不一樣的處理。在此不作詳細的介紹了,有興趣的能夠去看看
Promise.resolve('foo') // 等價於 new Promise(resolve => resolve('foo'))
此方法用於最後執行,不Promise的狀態如何,都會執行的操做(還有這種操做?)。在node中可用於關閉服務器或者數據庫鏈接。在瀏覽器中可用於須要在異步函數返回以後執行的操做。
var p = new Promise(function(resovle.reject){ resovle('is resovle'); }); p.then(function(res) { console.log(res) }).finally(function() { console.log('is finally'); })