或許你會(huì)問(wèn),這個(gè)操作明明是同步的,定義 Promise 里面的代碼都被立即執(zhí)行了,那么回調(diào)應(yīng)該緊接著 resolve 函數(shù)執(zhí)行,那么應(yīng)該先打印 “resolved” 而不應(yīng)該先打印 “end Promise”.
這個(gè)是 Promise 規(guī)范規(guī)定的,為了防止同步調(diào)用和異步調(diào)用同時(shí)存在導(dǎo)致的混亂
6、Promise 的鏈?zhǔn)秸{(diào)用(連貫操作)
前面我們講過(guò),Promise 的 then 方法以及 catch 方法返回的都是新的 Promise 對(duì)象,這樣我們可以非常方便的解決嵌套的回調(diào)函數(shù)的問(wèn)題, 也可以很方便的實(shí)現(xiàn)流程任務(wù)。
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); });
上面這段代碼很方便的實(shí)現(xiàn)了從 taskA 到 taskC 的有序執(zhí)行。
當(dāng)然你可以把 taskA - taskC 換成任何異步操作,如從后臺(tái)獲取數(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));
這樣用起來(lái)似乎很爽,但是有個(gè)問(wèn)題需要注意,我們說(shuō)過(guò)每個(gè) then() 方法都返回一個(gè)新的 Promise 對(duì)象,那既然是 Promise 對(duì)象,那肯定就有注冊(cè) onFulfilled 和 onRejected, 如果某個(gè)任務(wù)流程的 then() 方法鏈過(guò)長(zhǎng)的話,前面的任務(wù)拋出異常,會(huì)導(dǎo)致后面的任務(wù)被跳過(guò)。
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í)行的結(jié)果是:
Task A
Error: throw Error @ Task A
Final Task
顯然, 由于 A 任務(wù)拋出異常(執(zhí)行失?。?,導(dǎo)致 .then(taskB) 被跳過(guò),直接進(jìn)入 .catch 異常處理環(huán)節(jié)。
6.1 promise chain 中如何傳遞參數(shù)
上面我們簡(jiǎn)單闡述了 Promise 的鏈?zhǔn)秸{(diào)用,能夠非常有效的處理異步的流程任務(wù)。
但是在實(shí)際的使用場(chǎng)景中,任務(wù)之間通常都是有關(guān)聯(lián)的,比如 taskB 需要依賴(lài) taskA 的處理結(jié)果來(lái)執(zhí)行,這有點(diǎn)類(lèi)似 Linux 管道機(jī)制。 Promise 中處理這個(gè)問(wèn)題也很簡(jiǎn)單,那就是在 taskA 中 return 的返回值,會(huì)在 taskB 執(zhí)行時(shí)傳給它。
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);
搞定,就這么簡(jiǎn)單!
6.2 resolve 和 reject 參數(shù)
reject函數(shù)的參數(shù)通常是Error對(duì)象的實(shí)例,表示拋出的錯(cuò)誤;resolve函數(shù)的參數(shù)除了正常的值以外,還可能是另一個(gè) Promise 實(shí)例, 比如像上面的 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))
注意,這時(shí)p1的狀態(tài)就會(huì)傳遞給p2,也就是說(shuō),p1的狀態(tài)決定了p2的狀態(tài)。
如果p1的狀態(tài)是 pending,那么p2的回調(diào)函數(shù)就會(huì)等待p1的狀態(tài)改變;
如果p1的狀態(tài)已經(jīng)是 resolved 或者 rejected,那么p2的回調(diào)函數(shù)將會(huì)立刻執(zhí)行。
7、Promise 基本方法
ES6的Promise API提供的方法不是很多,下面介紹一下 Promise 對(duì)象常用的幾個(gè)方法.
7.1 Promise.prototype.catch()
Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(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 對(duì)象的錯(cuò)誤具有“冒泡”性質(zhì),會(huì)一直向后傳遞,直到被捕獲為止。也就是說(shuō),錯(cuò)誤總是會(huì)被下一個(gè)catch語(yǔ)句捕獲。 所以通常建議使用 catch 方法去捕獲異常,而不要用 then(null, function(error) {}) 的方式,因?yàn)檫@樣只能捕獲當(dāng)前 Promise 的異常
p.then(result => {console.log(result)}) .then(result => {console.log(result)}) .then(result => {console.log(result)}) .catch(error => { //捕獲上面三個(gè) Promise 對(duì)象產(chǎn)生的異常 console.log(error); });
跟傳統(tǒng)的try/catch代碼塊不同的是,如果沒(méi)有使用catch方法指定錯(cuò)誤處理的回調(diào)函數(shù),Promise 對(duì)象拋出的錯(cuò)誤不會(huì)傳遞到外層代碼,即不會(huì)有任何反應(yīng)。
通俗的說(shuō)法就是“Promise 會(huì)吃掉錯(cuò)誤”。
比如下面的代碼就出現(xiàn)這種情況
var p = new Promise(function(resolve, reject) { // 下面一行會(huì)報(bào)錯(cuò),因?yàn)閤沒(méi)有聲明 resolve(x + 2); }); p.then(() => {console.log("every thing is ok.");}); // 這行代碼會(huì)正常執(zhí)行,不會(huì)受 Promise 里面報(bào)錯(cuò)的影響 console.log("Ok, it's Great.");
7.2 Promise.all()
Promise.all方法用于將多個(gè) Promise 實(shí)例,包裝成一個(gè)新的 Promise 實(shí)例。用來(lái)處理組合 Promise 的邏輯操作。
var p = Promise.all([p1, p2, p3]);
上面代碼 p 的狀態(tài)由p1、p2、p3決定,分成兩種情況。
下面是一個(gè)具體的例子
// 生成一個(gè)Promise對(duì)象的數(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個(gè) Promise 實(shí)例的數(shù)組,只有這6個(gè)實(shí)例的狀態(tài)都變成 fulfilled,或者其中有一個(gè)變?yōu)?rejected, 才會(huì)調(diào)用 Promise.all 方法后面的回調(diào)函數(shù)。
7.3 Promise.race()
Promise.race方法同樣是將多個(gè)Promise實(shí)例,包裝成一個(gè)新的Promise實(shí)例。 與 Promise.all 不同的是,只要有一個(gè) promise 對(duì)象進(jìn)入 FulFilled 或者 Rejected 狀態(tài)的話,Promise.rece 就會(huì)繼續(xù)進(jìn)行后面的處理
var p = Promise.race([p1, p2, p3]);
上面代碼中,只要p1、p2、p3之中有一個(gè)實(shí)例率先改變狀態(tài),p的狀態(tài)就跟著改變。那個(gè)率先改變的 Promise 實(shí)例的返回值,就傳遞給p的回調(diào)函數(shù)。 Promise.race 方法的參數(shù)與 Promise.all 方法一樣,如果不是 Promise 實(shí)例,就會(huì)先調(diào)用 Promise.resolve 方法, 將參數(shù)轉(zhuǎn)為 Promise 實(shí)例,再進(jìn)一步處理。
下面是一個(gè)具體的例子
// `delay`毫秒后執(zhí)行resolve function timerPromisefy(delay) { return new Promise(function (resolve) { setTimeout(function () { resolve(delay); }, delay); }); } // 任何一個(gè)promise變?yōu)閞esolve或reject 的話程序就停止運(yùn)行 Promise.race([ timerPromisefy(1), timerPromisefy(32), timerPromisefy(64), timerPromisefy(128) ]).then(function (value) { console.log(value); // => 1 });
7.4 Promise.resolve()
Promise.resolve 方法有2個(gè)作用,一個(gè)就是前面我們說(shuō)的,它是通過(guò)靜態(tài)方法創(chuàng)建 Promise 實(shí)例的渠道之一, 另一個(gè)作用就是將 Thenable 對(duì)象轉(zhuǎn)換為 Promise 對(duì)象。
那么什么是 Thenable 對(duì)象呢?ES6 Promises里提到了Thenable這個(gè)概念,簡(jiǎn)單來(lái)說(shuō)它就是一個(gè)非常類(lèi)似promise的東西。 就像我們有時(shí)稱(chēng)具有 .length 方法的非數(shù)組對(duì)象為 Array like 一樣,Thenable 指的是一個(gè)具有 .then 方法的對(duì)象。
這種將 Thenable對(duì)象轉(zhuǎn)換為 Promise 對(duì)象的機(jī)制要求thenable對(duì)象所擁有的 then 方法應(yīng)該和Promise所擁有的 then 方法具有同樣的功能和處理過(guò)程, 在將 Thenable 對(duì)象轉(zhuǎn)換為 Promise 對(duì)象的時(shí)候,還會(huì)巧妙的利用 Thenable 對(duì)象原來(lái)具有的 then 方法。
到底什么樣的對(duì)象能算是 Thenable 的呢,最簡(jiǎn)單的例子就是 jQuery.ajax(),它的返回值就是 Thenable 的。
將 Thenable 對(duì)象轉(zhuǎn)換為 Promise 對(duì)象
var promise = Promise.resolve($.ajax('/json/comment.json'));// => promise對(duì)象 promise.then(function(value){ console.log(value); });
除了上面的方法之外,Promise.resolve方法的參數(shù)還有以下三種情況。
(1). 參數(shù)是一個(gè) Promise 實(shí)例
如果參數(shù)是Promise實(shí)例,那么Promise.resolve將不做任何修改、原封不動(dòng)地返回這個(gè)實(shí)例。
(2). 參數(shù)不是具有then方法的對(duì)象,或根本就不是對(duì)象
如果參數(shù)是一個(gè)原始值,或者是一個(gè)不具有then方法的對(duì)象,則Promise.resolve方法返回一個(gè)新的Promise對(duì)象,狀態(tài)為resolved。
var p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) });
上面代碼生成一個(gè)新的Promise對(duì)象的實(shí)例p。由于字符串Hello不屬于異步操作(判斷方法是字符串對(duì)象不具有then方法), 返回Promise實(shí)例的狀態(tài)從一生成就是resolved,所以回調(diào)函數(shù)會(huì)立即執(zhí)行。 Promise.resolve方法的參數(shù),會(huì)同時(shí)傳給回調(diào)函數(shù)。
(3). 不帶有任何參數(shù)
Promise.resolve方法允許調(diào)用時(shí)不帶參數(shù),直接返回一個(gè)resolved狀態(tài)的Promise對(duì)象。這個(gè)我們?cè)谏厦嬷v創(chuàng)建 Promise 實(shí)例的三種方法的時(shí)候就講過(guò)了
var p = Promise.resolve(); p.then(function () { // ... });
7.5 Promise.reject()
Promise.reject(reason)方法也會(huì)返回一個(gè)新的 Promise 實(shí)例,該實(shí)例的狀態(tài)為rejected。 需要注意的是,Promise.reject()方法的參數(shù),會(huì)原封不動(dòng)地作為 reject 的理由,變成后續(xù)方法的參數(shù)。這一點(diǎn)與 Promise.resolve 方法不一致。
const thenable = { then(resolve, reject) { reject('出錯(cuò)了'); } }; Promise.reject(thenable) .catch(e => { console.log(e === thenable) }) // true
上面代碼中,Promise.reject 方法的參數(shù)是一個(gè) thenable 對(duì)象,執(zhí)行以后,后面 catch 方法的參數(shù)不是 reject 拋出的“出錯(cuò)了”這個(gè)字符串, 而是 thenable 對(duì)象。
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com