http://caniuse.com/promises/embed/agents=desktopjavascript
Promise是抽象異步處理對象以及對其進行各類操做的組件。html
說到 javascript 異步操做,可能想到的是這樣:java
// 以 jQuery 的 ajax 爲例 $.get('/get_url', function(result, status) { if(status == 'success') { alert('success'); } if(status == 'error') { alert('error'); } });
對於 ajax 的 get 操做來講,是一個異步的過程,經過回調函數,在獲得返回的時候纔會去執行操做。ajax
可是試想一下當操做愈來愈多,回調裏面還要回調的時候,一層層回調函數是否是讓人抓狂,不論在代碼可讀性仍是編寫效率來看都是很麻煩的。
看一下咱們用 Promise 能夠怎麼作一個異步的操做:數組
// 這個 getData 是咱們預先實例好的一個 Promise 對象,如何處理這個對象咱們這裏不討論 var promise = getData('/get_url');![圖片描述][3] promise.then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
這樣的風格是否是會更好呢,執行一個 promise,而後 then 裏面傳入回調函數,若是願意,咱們能夠在 then 後面再更不少個 then,catch 能夠捕捉錯誤,看起來代碼清晰簡明多了。
因此,promise的功能是能夠將複雜的異步處理輕鬆地進行模式化。promise
new Promise(executor); new Promise(function(resolve, reject) { ... });
這裏的 executor
是咱們實例化一個 promise 對象時應該傳入的參數,這個參數只一個函數,這個函數接受兩個參數 resolve
和reject
。
兩個方法:瀏覽器
resolve(result)
在 promise 中執行這個方法表示成功,會在執行以後執行後面的 then 所傳入的函數,它接受到的參數也會被 then 裏面的函數接受到,通常來講參數爲執行結果成功時候的數據;併發
reject(error)
在 promise 中執行這個方法表示失敗,他通常接受一個 error 錯誤參數,會被後面的 catch 捕捉到錯誤執行。
demo:異步
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve('success'); }, 2000); }); }; testFoo().then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
在這裏咱們定義了一個 testFoo
函數,這個函數返回一個Promise
的實例化對象,兩秒以後會執行resolve('success');
,表示成功,傳入一個參數,在兩秒以後,咱們then裏面傳入的函數執行了,接收到了剛剛那個參數;可是catch裏面的函數並無執行,由於咱們沒有在 promise 裏面執行拒絕操做。函數
若是咱們在四秒以後執行 reject
操做呢:
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve('success'); }, 2000); setTimeout(function() { reject('error'); }, 4000); }); }; testFoo().then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
貌似只出現resolve
的結果,由於一個 promise 沒辦法作屢次結果操做。
咱們就這樣:
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { reject('error'); }, 4000); }); }; testFoo().then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
如今結果如咱們所預料了。
狀態分爲三種:
fulfilled
- Fulfilled 已完成,在 resolve
時,調用 then 的 onFulfilled
函數;
Rejected
- Rejected 拒絕,在reject
時,調用 then 的 onRejected
函數,或者 catch 裏面的函數;
unresolved
- Pending 等待,是 promise 初始化的時候的狀態。
promise 的 初始化狀態爲 unresolved,根據異步結果變爲 fulfilled 或者 Rejected,一旦變爲其中一個就不可改變,這也是咱們以前上面爲何執行了 resolve 以後再執行 reject 而沒有結果的緣由了。
Promise.resolve()
這個是promise的靜態方法
Promise.resolve(10).then(function(value){ console.log(value); });
這個方法會讓 Promise 當即進入 fulfilled 狀態,通常用來測試用。
Promise.reject()
相應着咱們有這個方法
Promise.reject('err').catch(function(err){ console.log(err); });
then(onFulfilled, onRejected)
這個方法具體是這樣的,傳入兩個函數,一個處理fulfilled狀態,另外一個處理Rejected狀態,通常使用咱們只傳入第一個函數,第二個放在 catch 來處理。
catch(onRejected)
處理Rejected狀態,能夠這麼理解catch(onRejected)
=promise.then(undefined, onRejected)
。
看看這個 demo:
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(1); }, 2000); }); }; testFoo().then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); return ++result; }).catch(function(error) { console.log(error); }); // 1 // 2 // 3
能夠看見結果,這個方法的流程是什麼樣的呢?
第一個 then 函數和以前講的同樣,處理 promise 的 fulfilled,第二個 then 的函數是處理前一個 then 函數處理完的結果,他們之間參數傳遞的途徑是前一個 then 函數 return 一個數據,而後後一個 then 函數接受到這個參數。
若是你願意,能夠再後面加不少不少個 then,他們的流程主要是這樣。
若是咱們把 catch 提早呢?
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(1); }, 2000); }); }; testFoo().then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); return ++result; }).catch(function(error) { console.log(error); }).then(function(result) { console.log(result); return ++result; }); // 1 // 2 // 3
能夠看出,結果同樣,說明catch只會在發生 reject 的時候調用。
那若是在中間的一個 then 中拋出一個異常呢?
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(1); }, 2000); }); }; testFoo().then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); throw new Error("throw Error") return ++result; }).then(function(result) { console.log(result); return ++result; }).catch(function(error) { console.log(error); }); // 1 // 2 // Error: throw Error
咱們在第二個then中拋出一個異常,然後當即被 catch 捕捉到,第三個 then 並無執行。
到這裏咱們想一下從then的傳參和捕捉異常來看,新加一個 then 只是註冊了一個回調函數那麼簡單嗎?
不不不,每次調用then都會返回一個新建立的promise對象,這就解釋了上面的一切緣由。
試想一個場景,咱們執行多個異步操做(ajax等等),可是咱們想在這幾個操做都完成的時候纔去執行一個函數。
若是按照傳統的方法來作,代碼會很亂很散,關鍵不優雅。讓咱們嘗試用 promise 。
先看這個 demo
var testFoo = function(time, value) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2); }, task2: function() { return testFoo(1.3, 3); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { function recordValue(results, value) { results.push(value); console.log(value); console.log(results); return results; } var pushValue = recordValue.bind(null, []); return tasks.task1().then(pushValue).then(tasks.task2).then(pushValue).then(tasks.task3).then(pushValue); }; main().then(function(value) { console.log(value); }); // [2, 3, 1]
這麼實現明顯看起來凌亂,特別對於 main 函數,可讀性也比較差。
那麼有沒有更優雅的方法呢,答案:有!?。
Promise.all
方法接受一個以 promise 對象爲元素的數組,在所有執行操做完成後纔回調用then裏面的方法,看代碼:
var testFoo = function(time, value) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2); }, task2: function() { return testFoo(1.3, 3); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { return Promise.all([tasks.task1(), tasks.task2(), tasks.task3()]); } main().then(function(result) { console.log(result); }); // [2, 3, 1]
咱們吧要執行的 promise 對象做爲數組的元素傳給 Promise.all()
,Promise.all().then()
裏面定義的函數接受到一個數組,元素是這幾個操做返回的值,順序和 promise 對象放入的順序同樣,好比第一個 promise 對象返回的值是2,那麼結果的第一個元素就是2。
而且每個promise是同時執行的--併發。
在全部 promise 的狀態爲 FulFilled 的時候纔會去執行 then 裏面的函數。
那麼捕捉異常呢?
修改一下例子:
var testFoo = function(time, value, err) { return new Promise(function(resolve, reject) { setTimeout(function() { if(err) { reject(err); return false; } resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2, 'error'); }, task2: function() { return testFoo(1.3, 3, 'error1'); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { return Promise.all([tasks.task1(), tasks.task2(), tasks.task3()]); } main().then(function(result) { console.log(result); }).catch(function(err) { console.log(err); }); // [2, 3, 1]
咱們讓其中2 promise 個拋出異常,看到捕捉到了那個異常,並且是捕捉到第一個異常就中止了。
說明 all 的操做是當全部 promise 狀態爲 FulFilled 的時候纔會執行 then 的操做。而一旦有一個 Rejected 就catch這個異常,而且中止。
他和 Promise.all 同樣,接受一個 promise 對象組成的數組,也是併發執行,可是 Promise.race 是隻要有一個promise對象進入 FulFilled 或者 Rejected 狀態的話,就會繼續進行後面的處理。
var testFoo = function(time, value) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2); }, task2: function() { return testFoo(1.3, 3); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { return Promise.race([tasks.task1(), tasks.task2(), tasks.task3()]); } main().then(function(result) { console.log(result); }); // 2
能夠看到,task1 最早完成,而後就拿到他的值進行 then 操做。
原文來自個人博客 http://qiutc.me/post/promise-learn-note.html歡迎你們關注~