ES6新特性:JavaScript中內建的延遲物件Promise
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
相關文章
- ES6中的Promise物件Promise物件
- 前端-JavaScript新特性(ES6)前端JavaScript
- JavaScript回顧00:字串,陣列,物件和ES6新特性JavaScript字串陣列物件
- ES6的Promise物件Promise物件
- JavaScript 中的延遲載入屬性模式JavaScript模式
- ES6——Promise 物件Promise物件
- 聊一聊Javascript中的Promise物件JavaScriptPromise物件
- ES6 中 Promise物件使用學習Promise物件
- JavaScript Promise物件JavaScriptPromise物件
- JavaScript Promise 物件JavaScriptPromise物件
- javascript ES6 新特性之 解構JavaScript
- ES6的Promise物件的使用Promise物件
- ES6語法——Promise物件Promise物件
- JavaScript ES6 特性JavaScript
- ES6中的新特性:Iterables和iterators
- ES6中let 和 const 的新特性
- ES6中的PromisePromise
- PostgreSQL中的複製延遲SQL
- ES6新特性
- JavaScript ES6 Promiss物件JavaScript物件
- JavaScript 在 Promise.then 方法裡返回新的 PromiseJavaScriptPromise
- [Javascript] Promise ES6 詳細介紹JavaScriptPromise
- es6中的promise解讀Promise
- JavaScript的內建物件JavaScript物件
- RabbitMQ延遲訊息的延遲極限是多少?MQ
- 測試 ES6 Promise 物件的鏈式傳值Promise物件
- 原生es6封裝一個Promise物件封裝Promise物件
- JavaScript ES2019 中的 8 個新特性JavaScript
- ES6中常用的10個新特性講解
- EF中延遲載入的那些事
- Spring Boot 2.2 中的延遲載入Spring Boot
- ES6中Promise 承諾物件封裝非同步操作解析Promise物件封裝非同步
- ES6新特性總結
- 玩轉ES6新特性
- ES6 新特性之SymbolSymbol
- 使用JavaScript ES6的新特性計算Fibonacci(非波拉契數列)JavaScript
- Restate:支援JavaScript/Java的Rust低延遲持久工作流RESTJavaScriptRust
- JS中的Promise 物件記錄JSPromise物件
- JavaScript物件導向—深入ES6的classJavaScript物件