您好,登錄后才能下訂單哦!
本篇文章為大家展示了Promise如何在Javascript中使用,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
什么是 Promise?
首先我們來了解 Promise 到底是怎么一回事
Promise 是抽象的異步處理對象,以及對其進行各種操作的組件。我知道這樣解釋你肯定還是不明白 Promise 是什么東西,你可以把 Promise 理解成一個 容器,里面裝著將來才會結束的一個事件的結果,這個事件通常是一個異步操作。
Promise最初被提出是在 E語言中, 它是基于并列/并行處理設計的一種編程語言。Javascript 在 ES6 之后也開始支持 Promise 特性了,用來解決異步操 的問題。這里順便解釋一下什么是 ES6, ECMAScript 是 Javascript 語言的國際標準,Javascript 是 ECMAScript 的有一個實現(xiàn), 而ES6(全稱 ECMAScript 6)是這個標準的一個版本。
3、Javascript 為什么要引入 Promise?
細心的你可能發(fā)現(xiàn)了我剛剛說了 Javascript 支持 Promise 實現(xiàn)是為了解決異步操作的問題。談到異步操作,你可能會說,Javascript 不是可以用回調 函數(shù)處理異步操作嗎? 原因就是 Promise 是一種更強大的異步處理方式,而且她有統(tǒng)一的 API 和規(guī)范,下面分別看看傳統(tǒng)處理異步操作和 Promise 處理 異步操作有哪些不同。
使用回調函數(shù)處理異步操作:
login("http://www.r9it.com/login.php", function(error, result){ // 登錄失敗處理 if(error){ throw error; } // 登錄成功時處理 });
Node.js等則規(guī)定在JavaScript的回調函數(shù)的第一個參數(shù)為 Error 對象,這也是它的一個慣例。 像上面這樣基于回調函數(shù)的異步處理如果統(tǒng)一參數(shù)使用規(guī)則的話,寫法也會很明了。 但是,這也僅是編碼規(guī)約而已,即使采用不同的寫法也不會出錯。 而Promise則是把類似的異步處理對象和處理規(guī)則進行規(guī)范化, 并按照采用統(tǒng)一的接口來編寫,而采取規(guī)定方法之外的寫法都會出錯。
使用 Promise 處理異步操作:
var promise = loginByPromise("http://www.r9it.com/login.php"); promise.then(function(result){ // 登錄成功時處理 }).catch(function(error){ // 登錄失敗時處理 });
通過上面兩個 demo 你會發(fā)現(xiàn),有了Promise對象,就可以將異步操作以同步操作的流程表達出來。 這樣在處理多個異步操作的時候還可以避免了層層嵌套的回調函數(shù)(后面會有演示)。 此外,Promise對象提供統(tǒng)一的接口,必須通過調用 Promise#then
和 Promise#catch
這兩個方法來結果,除此之外其他的方法都是不可用的,這樣使得異步處理操作更加容易。
4、基本用法
在 ES6 中,可以使用三種辦法創(chuàng)建 Promise 實例(對象)
(1). 構造方法
let promies = new Promise((resolve, reject) => { resolve(); //異步處理 });
Promise 構造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是 resolve 和 reject。它們是兩個函數(shù),由 JavaScript 引擎提供,不用自己部署。
(2). 通過 Promise 實例的方法,Promise#then 方法返回的也是一個 Promise 對象
promise.then(onFulfilled, onRejected);
(3). 通過 Promise 的靜態(tài)方法,Promise.resolve(),Promise.reject()
var p = Promise.resolve(); p.then(function(value) { console.log(value); });
4.1 Promise 的執(zhí)行流程
new Promise構造器之后,會返回一個promise對象;
為 promise 注冊一個事件處理結果的回調函數(shù)(resolved)和一個異常處理函數(shù)(rejected);
4.2 Promise 的狀態(tài)
實例化的 Promise 有三個狀態(tài):
Fulfilled: has-resolved, 表示成功解決,這時會調用 onFulfilled.
Rejected: has-rejected, 表示解決失敗,此時會調用 onRejected.
Pending: unresolve, 表示待解決,既不是resolve也不是reject的狀態(tài)。也就是promise對象剛被創(chuàng)建后的初始化狀態(tài).
上面我們提到 Promise 構造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是 resolve 和 reject.
resolve函數(shù)的作用是,將 Promise 對象的狀態(tài)從 未處理 變成 處理成功 (unresolved => resolved), 在異步操作成功時調用,并將異步操作的結果作為參數(shù)傳遞出去。
reject函數(shù)的作用是,將 Promise 對象的狀態(tài)從 未處理 變成 處理失敗 (unresolved => rejected), 在異步操作失敗時調用,并將異步操作報出的錯誤,作為參數(shù)傳遞出去。
Promise 實例生成以后,可以用 then 方法和 catch 方法分別指定 resolved 狀態(tài)和 rejected 狀態(tài)的回調函數(shù)。
以下是 Promise 的狀態(tài)圖
4.3 Promise 的基本特性
【1】 對象的狀態(tài)不受外界影響 Promise 對象代表一個異步操作,有三種狀態(tài):pending(進行中)、fulfilled(已成功)和rejected(已失?。?。 只有異步操作的結果,可以決定當前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)。 這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法變。
【2】 一旦狀態(tài)改變,就不會再變,任何時候都可以得到這個結果 Promise對象的狀態(tài)改變,只有兩種可能:從 pending 變?yōu)?fulfilled 和從 pending 變?yōu)?rejected。 只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結果,這時就稱為 resolved(已定型)。 如果改變已經(jīng)發(fā)生了,你再對 Promise 對象添加回調函數(shù),也會立即得到這個結果。 這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監(jiān)聽,是得不到結果的。
例如以下代碼, reject 方法是無效的
var promise = new Promise((fuck, reject) => { resolve("xxxxx"); //下面這行代碼無效,因為前面 resolve 方法已經(jīng)將 Promise 的狀態(tài)改為 resolved 了 reject(new Error()); }); promise.then((value) => { console.log(value); })
下圖是 Promise 的狀態(tài)處理流程圖
5、Promise 的執(zhí)行順序
我們知道 Promise 在創(chuàng)建的時候是立即執(zhí)行的,但是事實證明 Promise 只能執(zhí)行異步操作,即使在創(chuàng)建 Promise 的時候就立即改變它狀態(tài)。
var p = new Promise((resolve, reject) => { console.log("start Promise"); resolve("resolved"); }); p.then((value) => { console.log(value); }) console.log("end Promise");
打印的結果是:
start Promise
end Promise
resolved
或許你會問,這個操作明明是同步的,定義 Promise 里面的代碼都被立即執(zhí)行了,那么回調應該緊接著 resolve 函數(shù)執(zhí)行,那么應該先打印 “resolved” 而不應該先打印 “end Promise”.
這個是 Promise 規(guī)范規(guī)定的,為了防止同步調用和異步調用同時存在導致的混亂
6、Promise 的鏈式調用(連貫操作)
前面我們講過,Promise 的 then 方法以及 catch 方法返回的都是新的 Promise 對象,這樣我們可以非常方便的解決嵌套的回調函數(shù)的問題, 也可以很方便的實現(xiàn)流程任務。
var p = new Promise(function(resolve, reject) { resolve(); }); function taskA() { console.log("Task A"); } function taskB() { console.log("Task B"); } function taskC() { console.log("Task C"); } p.then(taskA()) .then(taskB()) .then(taskC()) .catch(function(error) { console.log(error); });
上面這段代碼很方便的實現(xiàn)了從 taskA 到 taskC 的有序執(zhí)行。
當然你可以把 taskA - taskC 換成任何異步操作,如從后臺獲取數(shù)據(jù):
var getJSON = function(url, param) { var promise = new Promise(function(resolve, reject){ var request = require('ajax-request'); request({url:url, data: param}, function(err, res, body) { if (!err && res.statusCode == 200) { resolve(body); } else { reject(new Error(err)); } }); }); return promise; }; var url = "login.php"; getJSON(url, {id:1}).then(result => { console.log(result); return getJSON(url, {id:2}) }).then(result => { console.log(result); return getJSON(url, {id:3}); }).then(result => { console.log(result); }).catch(error => console.log(error));
這樣用起來似乎很爽,但是有個問題需要注意,我們說過每個 then() 方法都返回一個新的 Promise 對象,那既然是 Promise 對象,那肯定就有注冊 onFulfilled 和 onRejected, 如果某個任務流程的 then() 方法鏈過長的話,前面的任務拋出異常,會導致后面的任務被跳過。
function taskA() { console.log("Task A"); throw new Error("throw Error @ Task A"); } function taskB() { console.log("Task B"); } function onRejected(error) { console.log(error); } function finalTask() { console.log("Final Task"); } var promise = Promise.resolve(); promise .then(taskA) .then(taskB) .catch(onRejected) .then(finalTask);
執(zhí)行的結果是:
Task A
Error: throw Error @ Task A
Final Task
顯然, 由于 A 任務拋出異常(執(zhí)行失?。瑢е?.then(taskB) 被跳過,直接進入 .catch 異常處理環(huán)節(jié)。
6.1 promise chain 中如何傳遞參數(shù)
上面我們簡單闡述了 Promise 的鏈式調用,能夠非常有效的處理異步的流程任務。
但是在實際的使用場景中,任務之間通常都是有關聯(lián)的,比如 taskB 需要依賴 taskA 的處理結果來執(zhí)行,這有點類似 Linux 管道機制。 Promise 中處理這個問題也很簡單,那就是在 taskA 中 return 的返回值,會在 taskB 執(zhí)行時傳給它。
function taskA() { console.log("Task A"); return "From Task A"; } function taskB(value) { console.log(value); console.log("Task B"); return "From Task B"; } function onRejected(error) { console.log(error); } function finalTask(value) { console.log(value); console.log("Final Task"); } var promise = Promise.resolve(); promise .then(taskA) .then(taskB) .catch(onRejected) .then(finalTask);
搞定,就這么簡單!
6.2 resolve 和 reject 參數(shù)
reject函數(shù)的參數(shù)通常是Error對象的實例,表示拋出的錯誤;resolve函數(shù)的參數(shù)除了正常的值以外,還可能是另一個 Promise 實例, 比如像上面的 getJSON() 方法一樣。
var p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) var p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error))
注意,這時p1的狀態(tài)就會傳遞給p2,也就是說,p1的狀態(tài)決定了p2的狀態(tài)。
如果p1的狀態(tài)是 pending,那么p2的回調函數(shù)就會等待p1的狀態(tài)改變;
如果p1的狀態(tài)已經(jīng)是 resolved 或者 rejected,那么p2的回調函數(shù)將會立刻執(zhí)行。
7、Promise 基本方法
ES6的Promise API提供的方法不是很多,下面介紹一下 Promise 對象常用的幾個方法.
7.1 Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發(fā)生錯誤時的回調函數(shù)。
p.then((val) => console.log('fulfilled:', val)) .catch((err) => console.log('rejected', err)); // 等同于 p.then((val) => console.log('fulfilled:', val)) .then(null, (err) => console.log("rejected:", err));
Promise 對象的錯誤具有“冒泡”性質,會一直向后傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。 所以通常建議使用 catch 方法去捕獲異常,而不要用 then(null, function(error) {}) 的方式,因為這樣只能捕獲當前 Promise 的異常
p.then(result => {console.log(result)}) .then(result => {console.log(result)}) .then(result => {console.log(result)}) .catch(error => { //捕獲上面三個 Promise 對象產(chǎn)生的異常 console.log(error); });
跟傳統(tǒng)的try/catch代碼塊不同的是,如果沒有使用catch方法指定錯誤處理的回調函數(shù),Promise 對象拋出的錯誤不會傳遞到外層代碼,即不會有任何反應。
通俗的說法就是“Promise 會吃掉錯誤”。
比如下面的代碼就出現(xiàn)這種情況
var p = new Promise(function(resolve, reject) { // 下面一行會報錯,因為x沒有聲明 resolve(x + 2); }); p.then(() => {console.log("every thing is ok.");}); // 這行代碼會正常執(zhí)行,不會受 Promise 里面報錯的影響 console.log("Ok, it's Great.");
7.2 Promise.all()
Promise.all方法用于將多個 Promise 實例,包裝成一個新的 Promise 實例。用來處理組合 Promise 的邏輯操作。
var p = Promise.all([p1, p2, p3]);
上面代碼 p 的狀態(tài)由p1、p2、p3決定,分成兩種情況。
只有p1、p2、p3的狀態(tài)都變成fulfilled,p的狀態(tài)才會變成fulfilled,此時p1、p2、p3的返回值組成一個數(shù)組,傳遞給p的回調函數(shù)。
只要p1、p2、p3之中有一個被rejected,p的狀態(tài)就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數(shù)。
下面是一個具體的例子
// 生成一個Promise對象的數(shù)組 var promises = [1,2,3,4,5,6].map(function (id) { return getJSON('/post/' + id + ".json"); }); Promise.all(promises).then(function (posts) { // ... }).catch(function(reason){ // ... });
上面代碼中,promises 是包含6個 Promise 實例的數(shù)組,只有這6個實例的狀態(tài)都變成 fulfilled,或者其中有一個變?yōu)?rejected, 才會調用 Promise.all 方法后面的回調函數(shù)。
7.3 Promise.race()
Promise.race方法同樣是將多個Promise實例,包裝成一個新的Promise實例。 與 Promise.all 不同的是,只要有一個 promise 對象進入 FulFilled 或者 Rejected 狀態(tài)的話,Promise.rece 就會繼續(xù)進行后面的處理
var p = Promise.race([p1, p2, p3]);
上面代碼中,只要p1、p2、p3之中有一個實例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數(shù)。 Promise.race 方法的參數(shù)與 Promise.all 方法一樣,如果不是 Promise 實例,就會先調用 Promise.resolve 方法, 將參數(shù)轉為 Promise 實例,再進一步處理。
下面是一個具體的例子
// `delay`毫秒后執(zhí)行resolve function timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } // 任何一個promise變?yōu)閞esolve或reject 的話程序就停止運行 Promise.race([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (value) { console.log(value); // => 1 });
7.4 Promise.resolve()
Promise.resolve 方法有2個作用,一個就是前面我們說的,它是通過靜態(tài)方法創(chuàng)建 Promise 實例的渠道之一, 另一個作用就是將 Thenable 對象轉換為 Promise 對象。
那么什么是 Thenable 對象呢?ES6 Promises里提到了Thenable這個概念,簡單來說它就是一個非常類似promise的東西。 就像我們有時稱具有 .length 方法的非數(shù)組對象為 Array like 一樣,Thenable 指的是一個具有 .then 方法的對象。
這種將 Thenable對象轉換為 Promise 對象的機制要求thenable對象所擁有的 then 方法應該和Promise所擁有的 then 方法具有同樣的功能和處理過程, 在將 Thenable 對象轉換為 Promise 對象的時候,還會巧妙的利用 Thenable 對象原來具有的 then 方法。
到底什么樣的對象能算是 Thenable 的呢,最簡單的例子就是 jQuery.ajax(),它的返回值就是 Thenable 的。
將 Thenable 對象轉換為 Promise 對象
var promise = Promise.resolve($.ajax('/json/comment.json'));// => promise對象 promise.then(function(value){ console.log(value); });
除了上面的方法之外,Promise.resolve方法的參數(shù)還有以下三種情況。
(1). 參數(shù)是一個 Promise 實例
如果參數(shù)是Promise實例,那么Promise.resolve將不做任何修改、原封不動地返回這個實例。
(2). 參數(shù)不是具有then方法的對象,或根本就不是對象
如果參數(shù)是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的Promise對象,狀態(tài)為resolved。
var p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) });
上面代碼生成一個新的Promise對象的實例p。由于字符串Hello不屬于異步操作(判斷方法是字符串對象不具有then方法), 返回Promise實例的狀態(tài)從一生成就是resolved,所以回調函數(shù)會立即執(zhí)行。 Promise.resolve方法的參數(shù),會同時傳給回調函數(shù)。
(3). 不帶有任何參數(shù)
Promise.resolve方法允許調用時不帶參數(shù),直接返回一個resolved狀態(tài)的Promise對象。這個我們在上面講創(chuàng)建 Promise 實例的三種方法的時候就講過了
var p = Promise.resolve(); p.then(function () { // ... });
7.5 Promise.reject()
Promise.reject(reason)方法也會返回一個新的 Promise 實例,該實例的狀態(tài)為rejected。 需要注意的是,Promise.reject()方法的參數(shù),會原封不動地作為 reject 的理由,變成后續(xù)方法的參數(shù)。這一點與 Promise.resolve 方法不一致。
const thenable = { then(resolve, reject) { reject('出錯了'); } }; Promise.reject(thenable) .catch(e => { console.log(e === thenable) }) // true
上面代碼中,Promise.reject 方法的參數(shù)是一個 thenable 對象,執(zhí)行以后,后面 catch 方法的參數(shù)不是 reject 拋出的“出錯了”這個字符串, 而是 thenable 對象。
上述內容就是Promise如何在Javascript中使用,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。