Promise的基本使用:
利用Promise是解決JS異步執行時候回調函數嵌套回調函數的問題, 更簡潔地控制函數執行流程;
通過new實例化Promise, 構造函數需要兩個參數, 第一個參數為函數執行成功以后執行的函數resolve, 第二個函數為函數執行失敗以后執行的函數reject:
new Promise(function(resolve , reject) { });
通過Promise,我們把回調函數用線性的方式寫出來,而不是一層套一層, 這個函數有四層回調;
fn("args", function(a) { fn1("foo", function(b) { fn2("bar", function(c) { fn3("baz", function(d) { alert("回調成功,獲知的內容為:"+a+b+c+d) }) }) }) })
以上的Demo只有包含成功的回調, 如果失敗的回調也算的話, 也就更麻煩了;
如果使用Promise的方式,我們可以改裝成線性的代碼, 更加符合閱讀的習慣,只要在then函數下寫邏輯即可;
new Promise(function(resolve , reject) { resolve(1); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(2); }); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(3); }); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(4); }); }).then(function(val) { console.log(val); });
這是一個ajax異步獲取數據的例子, 我們使用了回調函數;
<html> <head> <meta charset="utf-8"> </head> <body> <script> var callback = function(res) { console.log(res); }; var ajax = function(url, callback) { var r = new XMLHttpRequest(); r.open("GET", url, true); r.onreadystatechange = function () { if (r.readyState != 4 || r.status != 200) return; var data = JSON.parse(r.responseText); callback(data); }; r.send(); }; //執行請求: ajax("http://www.filltext.com?rows=10&f={firstName}", callback); //再做別的事情; </script> </body> </html>
因為ES6內置了Promise, 我們可以把以上的callback改寫成promise的方式, 首先ajax函數返回一個Promise對象;
<html> <head> <meta charset="utf-8"> </head> <body> <script> var callback = function(res) { console.log(res); }; var ajax = function(url) { return new Promise(function(resolve, reject) { var r = new XMLHttpRequest(); r.open("GET", url, true); r.onreadystatechange = function () { if (r.readyState != 4 || r.status != 200) return; var data = JSON.parse(r.responseText); resolve(data); }; r.send(); }) }; //執行請求: ajax("http://www.filltext.com?rows=10&f={firstName}").then(function(data) { callback(data); }); //再做別的事情; </script> </body> </html>
Promise實例的三種狀態:
每一個實例化的Promise都有三個狀態;pending(等待) rejected(拒絕) resolved(解決) ,默認的狀態為pending,如果執行了resolve(), 那么這個promise的狀態會變為resolve,如果執行了reject(), 那么這個promise的狀態就會變成rejected, 而且這些狀態是不可撤銷的,一經更改,不會再變了;
then方法:
promise有一個then方法,then方法接收兩個參數, 第一個為函數的成功回調, 第二個為函數的失敗回調:
var promise = new Promise(function(resolve , reject) { resolve(); //執行成功回調; }); console.log(0); promise.then(function(val) { console.log(1); }, function() { console.log("失敗"); }); console.log("2");
var promise = new Promise(function(resolve , reject) { reject(); }); console.log(0); promise.then(function(val) { console.log(1); }, function() { console.log("失敗"); }); console.log("2");
then方法每一次都是返回不同的Promise實例,then的第一個參數是成功回調, 這個成功回調的參數為: 上一個Promise實例執行resolve方法的參數;
一般來說, then方法會返回當前的promise, 如果在then方法里面return 一個新的Promise實例,那么此時的then返回的就是新的Promise實例, 利用這個特性,就可以實現多層回調;
new Promise(function(resolve , reject) { resolve(1); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(2); }); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(3); }); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(4); }); }).then(function(val) { console.log(val); });
不管代碼是異步還是同步的, 都可以用Promise的then方法, 同步的代碼直接寫在then方法第一個參數, 把需要參數通過resolve傳給下一個then方法,
如果是異步的代碼, 就直接return一個Promise實例:
new Promise(function(resolve , reject) { resolve(1); }).then(function(val) { console.log(val); return 2; }).then(function(val) { console.log(val); return 3; }).then(function(val) { console.log(val); return new Promise(function(resolve,reject) { //異步操作些這里 resolve(4); }); }).then(function(val) { console.log(val); return 5; }).then(function(val) { console.log(val); });
catch方法:
catch方法和失敗回調時一樣的, 如果上一個異步函數拋出了錯誤了, 錯誤會被捕獲, 并執行catch方法或者失敗回調;
var promise = new Promise(function(resolve , reject) { resolve(); //執行成功回調; }); console.log(0); promise.then(function(val) { console.log("成功"); throw new Error("heheda"); }).catch(function(e) { console.log(e); }).then(function() { console.log("繼續執行"); });
Promise中的錯誤是會一層層傳遞的, 如果錯誤沒有沒有被捕獲, 會一直傳遞給下一個promise對象, 直到被捕獲為止, 然后繼續往下執行:
new Promise(function(resolve , reject) { resolve(1); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { throw new Error("err"); }); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(3); }); }).then(function(val) { console.log(val); return new Promise(function(resolve , reject) { resolve(4); }); }).then(function(val) { console.log(val); }).catch(function(err) { console.log(err); }).then(function() { console.log("繼續執行") })
構造函數Promise的四個方法:
構造函數Promise有四個方法, Promise.all, Promise.race, Promise.reject, Promise.resolve:
Promise.all(iterable)
返回一個promise對象,當iterable參數里所有的promise都被解決后,該promise也會被解決
要注意all方法是Promise函數的方法,不是實例的方法, 參數是一個數組, 數組里面全是Promise的實例 :
var p0 = new Promise(function(resolve) { setTimeout(function() { resolve(0) },1000); }) var p1 = new Promise(function(resolve) { setTimeout(function() { resolve(1) },2000); }) var p2 = new Promise(function(resolve) { setTimeout(function() { resolve(2) },3000); }) Promise.all([p0,p1,p2]).then(function(arr) { console.log(arr) })
Promise.race(iterable)
當iterable參數里的任意一個子promise被成功或失敗后,父promise馬上也會用子promise的成功返回值或失敗詳情作為參數調用父promise綁定的相應句柄,并返回該promise對象。
Promise.reject(reason)
調用Promise的rejected句柄,并返回這個Promise對象。
Promise.resolve(value)
用成功值value解決一個Promise對象。如果該value為可繼續的(thenable,即帶有then方法),返回的Promise對象會“跟隨”這個value,采用這個value的最終狀態;否則的話返回值會用這個value滿足(fullfil)返回的Promise對象。
官方的例子:
<html> <head> <meta charset="utf-8"> </head> <body> <div id="log"></div> <script> 'use strict'; var promiseCount = 0; function testPromise() { var thisPromiseCount = ++promiseCount; var log = document.getElementById('log'); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 開始(同步代碼開始)<br/>'); // 我們創建一個新的promise: 然后用'result'字符串解決這個promise (3秒后) var p1 = new Promise(function (resolve, reject) { // 解決函數帶著解決(resolve)或拒絕(reject)promise的能力被執行 log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise開始(異步代碼開始)<br/>'); // 這只是個創建異步解決的示例 window.setTimeout(function () { // 我們滿足(fullfil)了這個promise! resolve(thisPromiseCount) }, Math.random() * 2000 + 1000); }); // 定義當promise被滿足時應做什么 p1.then(function (val) { // 輸出一段信息和一個值 log.insertAdjacentHTML('beforeend', val + ') Promise被滿足了(異步代碼結束)<br/>'); }); log.insertAdjacentHTML('beforeend', thisPromiseCount + ') 建立了Promise(同步代碼結束)<br/><br/>'); } testPromise(); </script> </body> </html>
既然有了Promise , 我們就可以把封裝XMLHttpRequest封裝成GET方法, 方便使用:
function get(url) { // Return a new promise. return new Promise(function(resolve, reject) { // Do the usual XHR stuff var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { // This is called even on 404 etc // so check the status if (req.status == 200) { // Resolve the promise with the response text resolve(req.response); } else { // Otherwise reject with the status text // which will hopefully be a meaningful error reject(Error(req.statusText)); } }; // Handle network errors req.onerror = function() { reject(Error("Network Error")); }; // Make the request req.send(); }); }
然后使用:
get('story.json').then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
假數據的地址可以自己設置, 可以通過控制臺請求, 注意跨域的問題;
封裝XMLHttpRequest成Promise異步加載圖片的案例:https://github.com/mdn/promises-test/blob/gh-pages/index.html
其他:
以上只是Promise的一些基礎知識, 還有一些其他的知識點, 因為能力有限不一一介紹了(Promise.resolve的不同參數, 與Generator一起使用, Promise的附加方法, 等等等等);
把Promise的運行流程畫出來, 對Promise的理解會好一點, Promise還是比較繞的
瀏覽器支持情況:
Chrome 32, Opera 1,Firefox 29, Safari 8 ,Microsoft Edge, 這些瀏覽器以上都默認支持;
參考
Promises/A+ 規范: https://promisesaplus.com/
ES6Promises的polyfill : https://github.com/stefanpenner/es6-promise#readme
html5rocks:http://www.html5rocks.com/en/tutorials/es6/promises/
阮老師:http://es6.ruanyifeng.com/#docs/promise
作者: NONO
出處:http://www.cnblogs.com/diligenceday/
QQ:287101329
微信:18101055830
文章列表