您好,登錄后才能下訂單哦!
這篇文章給大家介紹ES6中promise的作用是什么,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
第一部分:什么是Promise
看本文的最后一個例子,迅速理解。
Promise是ES6中的一個內置的對象(實際上是一個構造函數,通過這個構造函數我們可以創建一個Promise對象),它是為了解決異步問題的。Promise的英文意思是承諾。
Promise的特點如下:
?Promise有三種狀態:Pending(進行中)、Resolved(已完成)、Rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這就是Promise。
?Promise一共有三種狀態,但是他們之間是如何轉化的呢? 其一: 從Pending(進行中)到Resolved(完成)。其二: 從Pending(進行中)到Rejected(已失敗)。 且只有這兩種形式的轉變,即使是Promise對象的結果也無力回天了。
但是Promise也是有一定的缺點的,如在Pengding時,我們無法取消狀態,另外,我們沒法判斷Pending究竟是剛剛開始的Pending還是即將要完成的Pending。
第二部分:使用Promise
前言:
在下面例子的講解中,我們需要使用到setTimeout函數,這里首先說明setTimeout的一些重要用法。一般我們常用的如下所示:
setTimeout(func(){}, 1000);
即在1000ms之后執行func函數,但是其實setTimeout還可以傳入更多的參數。這篇博客做了講解,而這里為了了解下面的例子我們只需要知道,在chrome中可以傳入第三個、第四個參數....作為func()函數的參數傳遞進去,舉例如下:
setTimeout(function (a, b) { console.log(a + b); }, 1000, 20, 50);
最終的輸入結果是:70
下面的代碼創建了一個Promise實例:
var promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } });
其中,由于Promise是構造函數,所以我們使用new來創建一個對象即可, 值得注意的是:function(resolve, reject){}這個函數是必須要寫的,否則就不是Promise了。
這個函數是為了初始化Promise對象,其中這個函數接受了兩個函數作為參數, 如果在函數體中我們執行了resolve函數,那么Promise的狀態就會由pending轉化為resolved(或fullfilled,兩者是相同的),類似的,如果我們執行了reject函數,pending就會變成 rejected。
注意: 這個例子的if語句不是必要的,這里想要表達的意思是如果得到了異步成功的相關結果,我們就將調用resolve,將pending轉化為resolved,并且將異步成功后的value值傳遞進去以便后面使用,說是以便后面使用是因為Promise還有一個then()方法,即可以定義在異步成功(或失敗)之后需要做的事情。這也就是resolve和reject內置函數存在的意義了。
當創建了這個Promise對象之后,就一定會有一個結果了,但是成功和失敗還是不確定的,我們需要根據判斷結果的成功和失敗來做不同的事情,于是用到了then()方法,如下所示:
promise.then(function(value) { // success }, function(error) { // failure });
下面是一個例子,并做了詳盡的說明:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>testsettimeout</title> </head> <body> <script> var promise = new Promise(function (resolve, reject) { console.log("good"); var a = 10; var b = a + 25; if ( b === 35 ) { // 一旦異步執行成功,我們就調用內置的 resolve函數,將pending狀態轉化為resolved,并且傳入我們希望傳出的執行成功后的結果。 // 注意: 這里一旦狀態轉變,那么后面就一定會調用then方法中的第一個參數的函數,然后將我們傳入給resolve的參數傳給then方法中的第一個方法作為參數,我們就可以在then的第一個方法中使用了。 resolve(b); } else { reject("異步執行失敗"); } }); promise.then(function (value) { console.log("異步執行成功,輸出執行結果:" + value); }, function (error) { console.log("異步執行失敗,輸出執行結果:" + error); }); </script> </body> </html>
而阮一峰老師所列的下面的這個例子可以清楚的看出promise就是異步的:
let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('Resolved.'); }); console.log('Hi!');
最終的輸出結果是: Promise Resolved Hi
分析: 從這個例子中可以看出promise的異步,因為前面的兩部分代碼還沒有執行,就已經輸出了Hi。另外可以確定的是 Resolved 一定是在 Promise之后輸出的,這個順序是不可能有問題的。
下面的例子是一個異步添加圖片的url的例子
function loadImageAsync(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.onload = function () { resolve(image); }; image.onerror = function() { reject(new Error('Could not load image at' + url)); } image.src = url; }); }
如果加載成功就使用resolve方法,如果失敗就使用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); });
這里應該大家都可以看懂,值得注意的是:handler這個處理函數的使用在這里顯得很巧妙。
第三部分: Promise.prototype.then()
在上一部分,我們實際上已經介紹了then()方法,而這里需要強調的有兩點。
第一: then()方法是Promise原型上定義的方法。
第二:then()方法支持鏈式調用,上一個then()方法調用后返回的結果會傳給下一個then方法中。
第一:我們再chrome中輸入 Promise.prototype可以看到下面的例子:
可以看出在Promise的原型中確實是由then方法的。(注:比如我們想看Array這個內置對象有哪些方法,我們就可以直接在chrome中輸入Array.prototype,然后就可以看到對應方法的列表了)
第二: then()的作用是為Promise實例添加狀態改變時的回調函數。前面說過,then方法的第一個參數是Resolved狀態的回調函數,第二個參數(可選)是Rejected狀態的回調函數。
then()由于支持鏈式調用,所以也可以寫成下面這樣:
getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // ... });
第一個回調函數完成以后,會將返回結果作為參數,傳入第二個回調函數。
看下面的這里例子:
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); });
即第一個then又返回了一個promise,如何這個promise的狀態變成了 Resolved,那么就會執行第二個then的第一個函數, 如果變成了 Rejected,就會執行第二個第二個函數。
第四部分: Promise.prototype.catch()
Promise.prototype.catch()方法實際上是then(null, rejection)方法的別名, 這里使用catch()純粹是為了便于使用和理解。
getJSON('/posts.json').then(function(posts) { // ... }).catch(function(error) { // 處理 getJSON 和 前一個回調函數運行時發生的錯誤 console.log('發生錯誤!', error); });
在之前的例子中,我們講解then()方法接受兩個參數,第一個參數是pending變成resolved之后執行的函數,它是必選的; 第二個參數是pending變成rejected之后執行的函數,它是可選的。
我們建議,最后不要使用第二個參數,取而代之我們最好使用catch(),如下所示:
// bad promise .then(function(data) { // success }, function(err) { // error }); // good promise .then(function(data) { //cb // success }) .catch(function(err) { // error });
值得注意的是:catch()方法返回的還是一個Promise對象,我們可以在后面繼續使用then進行鏈式調用。
第五部分:Promise.all()
Promise.all()方法用于將多個Promise實例包裝成一個新的Promise實例,如下所示:
var p = Promise.all([p1, p2, p3]);
其中的p1,p2,p3都是Promise對象實例,如果不是,就會先調用下面講到的Promise.resolve方法,將參數轉為Promise實例,再進一步處理。
p的狀態由p1,p2,p3決定, 分成下面的兩種情況:
?只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
?只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
讓我們看看下面的具體的例子:
// 生成一個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){ // ... });
在這個例子中,通過數組的map生成了6個Promise對象,然后作為參數傳遞給了Promise.all() 只有這6個Promise對象最終返回的都是 resolved時, 才會調用Promise.all()后面then()方法。
第六部分:Promise.race()
該方法同樣是將多個Promise實例包裝成一個新的Promise實例,如下:
var p = Promise.race([p1, p2, p3]);
在上面的代碼中,如果有一個Promise率先改變狀態,p的狀態就會跟著改變。
第七部分: Promise.resolve()
如果我們希望將一個現有對象轉化為Promise對象,我們就可以使用Promise.resolve()方法。根據參數的不同可以分為4種情況:
(1)參數是一個Promise實例
如果參數是Promise實例,那么Promise.resolve將不做任何修改、原封不動地返回這個實例。
(2)參數是一個thenable對象
如果參數是一個thenable對象,即具有then()方法的對象,那么Promise.resolve()就會將該對象立即轉化成 Promise對象那個,并立即執行then方法。
(3)參數不是具有then方法的對象,或根本就不是對象
如果參數是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的Promise對象,狀態為Resolved。
(4)不帶有任何參數
第八部分: Promise.reject()
Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態為rejected。
第九部分: 理解Promise
它的作用就是如果前面的一段代碼非常的耗時,就會阻塞后面的某個代碼片段。 所以我們希望他不要阻塞后面的代碼,那么就讓他異步好了。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>testsettimeout</title> </head> <body> <div class="wrap"> </div> <script> console.time("time"); for (var i = 0; i < 50000; i++) { var li = document.createElement("li"); document.querySelector(".wrap").append(li); } console.timeEnd("time"); var a = 10; var b = a + 25; console.log( a); console.log(b); console.timeEnd("time"); </script> </body> </html>
最后的輸出結果是:
可以看到前面的代碼在阻塞后面的代碼。 導致后面的代碼無法運行。
因為我們希望這個耗時的代碼越快執行越好,會非常嚴重的影響用戶體驗,所以把它放在了最前面,如果它沒有什么用,就放在了最后面了。
但是后面的代碼我們也希望盡快執行啊,那么該怎么辦呢? 如果可以讓前面的代碼單獨在一個線程上運行, 而不影響后面的代碼運行就好了。
setTimeout()就可以做到,如下所示:
console.time("time"); setTimeout(function() { for (var i = 0; i < 50000; i++) { var li = document.createElement("li"); document.querySelector(".wrap").append(li); } console.timeEnd("time"); }, 0); var a = 10; var b = a + 25; console.log(a); console.log(b); console.timeEnd("time");
最終的輸出結果如下:
可以看到在setTimeout中的函數一開始就執行了。
但是為什么setTimeout的內容執行的這么慢呢? 我們再看一個例子
console.time("time"); setTimeout(function() { console.timeEnd("time"); }, 0);
結果如下:
time: 2.234ms
即 setTimeout 自身就會先消耗一定的時間。
ok,那么說了半天Promise到底怎么和他們比較呢? 他的價值在哪里呢?
價值都在這里了!看下面的這個例子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>testsettimeout</title> </head> <body> <div class="wrap"> </div> <script> let promise = new Promise(function(resolve, reject) { console.log('Promise! 我是Promise對象里的函數,我最早出現是因為我要給后面一個耗時的家伙提供數據a'); var a = 20; resolve(a); }); promise.then(function(value) { console.log('哈哈哈,我得到數據a了,要開始執行嘍。我是一個非常耗時的操作,依賴前面的數據,但是我想快一點執行,這有利于用戶體驗,所以別把我放在后頭啊;我被放在了一個新的線程上,不會阻塞別人的'); for (var i = 0; i < 1000000; i++) { var li = document.createElement("li"); document.querySelector(".wrap").appendChild(li); } console.log("執行完畢"); }); console.log('Hi! 我是什么都不依賴的程序,但我也想快一點執行,不能委屈我啊'); </script> </body> </html>
說明: 在已經執行過的情況下,錄制屏幕,然后按下刷新鍵,可以發現,在new Promise里的函數是立即執行的,緊接著是promise.then()之后的函數立即執行,而沒有等待then()函數,最后進入then()函數,說了一堆廢話,最后大約2 ~ 3s之后執行結束。故promise.then()函數才是真正的異步執行。
promise實現:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>promise</title> </head> <body> <script> function Mypromise(fn) { this.state = 'pending'; this.value = void 0; this.doneList = []; this.failList = []; // 在創建一個promise的時候,需要傳遞一個fn函數,接受參數resolve和reject。 // resolve和reject目前還沒有定義,因為用戶是直接調用的,如resolve('success'),所以這個resolve方法是需要我們自己來定義的。 var self = this; function resolve(value) { // 一旦resolve,狀態機的狀態由pending -> resolved // 并且在resolve的時候狀態必須是pending,才能轉換狀態。 // 異步執行所有回調函數 setTimeout(function () { if (self.state == 'pending') { self.state = 'resolved' self.value = value; // 一旦成功,我們就可以執行所有promise上掛載的 doneList 中所有的回調函數了,并將value值傳遞過去 for (var i = 0; i < self.doneList.length; i++) { self.doneList[i](value); } } }, 0); } function reject(reason) { // 一旦reject,狀態機的狀態由pending -> rejected // 在reject的時候狀態必須是pending,才能轉化狀態。 // 異步執行所有回調函數 setTimeout(function () { if (self.state == 'pending') { self.state = 'rejected' self.value = reason; // 一旦失敗,我么就可以把所有的 failList 調用了,并且傳遞 reason for (var i = 0; i < self.failList.length; i++) { self.failList[i](reason); } } }, 0); } fn(resolve, reject); } Mypromise.prototype = { constructor: Mypromise, // then方法接受成功時的回調和失敗時的回調 // 實際上,這里的then方法就像路由中的注冊路由一樣,是一個注冊的過程,而沒有真正的調用。 // 并且then是支持鏈式調用的,所以then應該返回一個promise。 如果返回舊的,那么因為狀態不能改變,所以沒有意義,所以我么一定要返回一個新的promise,這樣這個狀態機才可以再次正常的工作。 then: function (onResolved, onRejected) { var self = this; // 對于then而言,最終要返回一個新的promise,這樣才支持鏈式調用。 var promise2; // 這里要做一個判斷,看傳遞進來的是否是一個函數,如果是,則不變,如果不是,就拒絕。 // 如果是promise.then().then().then(function (value) {}); 我們還希望拿到value,那么就要把value在即使沒有用到onResolved的時候也傳遞下去。對于reason也是如此。 onResolved = typeof onResolved == 'function' ? onResolved : function (value) {return value;} onRejected = typeof onRejected == 'function' ? onRejected : function (reason) { return reason;} // 下面這一部分是比較核心的內容,因為then最終要返回一個promise,但是,這個promise究竟應該怎么返回呢? 如果在then中,用戶就返回了promise,那么我們就用戶的,如果用戶用的不是promise,那么我么就要自己封裝好這個promise了。 // 注意: promise也是需要異步調用的,所以可以使用promise進行封裝。 switch(this.state) { case 'resolved': // 如果resolved,則返回一個新的promise return promise2 = new Mypromise(function (resolve, reject) { setTimeout(function () { try { // 這里相當于直接就給執行了,看看返回的是什么值,如果是promise,那么直接就使用這個promise了。 var x = onResolved(self.value); if (x instanceof Mypromise) { x.then(resolve, reject); } } catch (e) { reject(e); } }, 0) }) case 'rejected': // 如果rejected,同樣也需要返回一個新的promise return promise2 = new Mypromise( function (resolve, reject) { setTimeout(function () { try { var x = onRejected(self.value); if (x instanceof Mypromise) { x.then(resolve, reject); } } catch (e) { reject(e); } }, 0); }) // 如果是pending狀態,我們就不能確定使用什么,等到狀態確定之后才能決定。 case 'pending': return promise2 = new Mypromise(function () { // 注意:一般then都是執行的這里, 即在then的時候進行注冊,把相應的成功的和失敗的都會初測在這里,push的是一個函數,所以這里的onResolved還是沒有執行的。 setTimeout(function () { self.doneList.push(function (value) { try { var x = onResolved(self.value) if (x instanceof Mypromise) { x.then(resolve, reject) } else { onResolved(value); } } catch (e) { reject(e) } }); console.log(self.doneList) self.failList.push(function (value) { try { var x = onRejected(self.data); if (x instanceof Mypromise) { x.then(resolve, reject); } } catch (e) { } }); }, 0); }) default: console.log(this.state); return; } }, catch: function (onRejected) { return this.then(null, onRejected); } } var promise = new Mypromise(function (resolve, reject) { if (5 > 3) { resolve('success'); } }); promise.then(function (value) { console.log('哈哈哈哈或'); }); </script> </body> </html>
關于ES6中promise的作用是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。