摘要:說到異步操作,可能想到的是這樣以的為例對于的操作來說,是一個(gè)異步的過程,通過回調(diào)函數(shù),在得到返回的時(shí)候才會去執(zhí)行操作。
瀏覽器支持
http://caniuse.com/promises/embed/agents=desktop
Promise是抽象異步處理對象以及對其進(jìn)行各種操作的組件。
說到 javascript 異步操作,可能想到的是這樣:
// 以 jQuery 的 ajax 為例 $.get("/get_url", function(result, status) { if(status == "success") { alert("success"); } if(status == "error") { alert("error"); } });
對于 ajax 的 get 操作來說,是一個(gè)異步的過程,通過回調(diào)函數(shù),在得到返回的時(shí)候才會去執(zhí)行操作。
但是試想一下當(dāng)操作越來越多,回調(diào)里面還要回調(diào)的時(shí)候,一層層回調(diào)函數(shù)是不是讓人抓狂,不論在代碼可讀性還是編寫效率來看都是很麻煩的。
看一下我們用 Promise 可以怎么做一個(gè)異步的操作:
// 這個(gè) getData 是我們預(yù)先實(shí)例好的一個(gè) Promise 對象,如何處理這個(gè)對象我們這里不討論 var promise = getData("/get_url");![圖片描述][3] promise.then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
這樣的風(fēng)格是不是會更好呢,執(zhí)行一個(gè) promise,然后 then 里面?zhèn)魅牖卣{(diào)函數(shù),如果愿意,我們可以在 then 后面再更很多個(gè) then,catch 可以捕捉錯(cuò)誤,看起來代碼清晰簡明多了。
所以,promise的功能是可以將復(fù)雜的異步處理輕松地進(jìn)行模式化。
new Promise(executor); new Promise(function(resolve, reject) { ... });
這里的 executor 是我們實(shí)例化一個(gè) promise 對象時(shí)應(yīng)該傳入的參數(shù),這個(gè)參數(shù)只一個(gè)函數(shù),這個(gè)函數(shù)接受兩個(gè)參數(shù) resolve和reject。
兩個(gè)方法:
resolve(result) 在 promise 中執(zhí)行這個(gè)方法表示成功,會在執(zhí)行之后執(zhí)行后面的 then 所傳入的函數(shù),它接受到的參數(shù)也會被 then 里面的函數(shù)接受到,一般來說參數(shù)為執(zhí)行結(jié)果成功時(shí)候的數(shù)據(jù);
reject(error) 在 promise 中執(zhí)行這個(gè)方法表示失敗,他一般接受一個(gè) error 錯(cuò)誤參數(shù),會被后面的 catch 捕捉到錯(cuò)誤執(zhí)行。
demo:
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve("success"); }, 2000); }); }; testFoo().then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
在這里我們定義了一個(gè) testFoo 函數(shù),這個(gè)函數(shù)返回一個(gè)Promise的實(shí)例化對象,兩秒之后會執(zhí)行resolve("success");,表示成功,傳入一個(gè)參數(shù),在兩秒之后,我們then里面?zhèn)魅氲暮瘮?shù)執(zhí)行了,接收到了剛剛那個(gè)參數(shù);但是catch里面的函數(shù)并沒有執(zhí)行,因?yàn)槲覀儧]有在 promise 里面執(zhí)行拒絕操作。
如果我們在四秒之后執(zhí)行 reject 操作呢:
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve("success"); }, 2000); setTimeout(function() { reject("error"); }, 4000); }); }; testFoo().then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
貌似只出現(xiàn)resolve的結(jié)果,因?yàn)橐粋€(gè) promise 沒辦法做多次結(jié)果操作。
我們就這樣:
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { reject("error"); }, 4000); }); }; testFoo().then(function(result) { console.log(result); }).catch(function(error) { console.log(error); });
現(xiàn)在結(jié)果如我們所預(yù)料了。
PromiseStatus 狀態(tài)狀態(tài)分為三種:
fulfilled - Fulfilled 已完成,在 resolve時(shí),調(diào)用 then 的 onFulfilled函數(shù);
Rejected - Rejected 拒絕,在reject時(shí),調(diào)用 then 的 onRejected函數(shù),或者 catch 里面的函數(shù);
unresolved - Pending 等待,是 promise 初始化的時(shí)候的狀態(tài)。
promise 的 初始化狀態(tài)為 unresolved,根據(jù)異步結(jié)果變?yōu)?fulfilled 或者 Rejected,一旦變?yōu)槠渲幸粋€(gè)就不可改變,這也是我們之前上面為什么執(zhí)行了 resolve 之后再執(zhí)行 reject 而沒有結(jié)果的原因了。
方法概述 快捷方法Promise.resolve() 這個(gè)是promise的靜態(tài)方法
Promise.resolve(10).then(function(value){ console.log(value); });
這個(gè)方法會讓 Promise 立即進(jìn)入 fulfilled 狀態(tài),一般用來測試用。
Promise.reject()相應(yīng)著我們有這個(gè)方法
Promise.reject("err").catch(function(err){ console.log(err); });實(shí)例方法
then(onFulfilled, onRejected)這個(gè)方法具體是這樣的,傳入兩個(gè)函數(shù),一個(gè)處理fulfilled狀態(tài),另一個(gè)處理Rejected狀態(tài),一般使用我們只傳入第一個(gè)函數(shù),第二個(gè)放在 catch 來處理。
catch(onRejected)處理Rejected狀態(tài),可以這么理解catch(onRejected)=promise.then(undefined, onRejected)。
鏈?zhǔn)秸{(diào)用看看這個(gè) demo:
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(1); }, 2000); }); }; testFoo().then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); return ++result; }).catch(function(error) { console.log(error); }); // 1 // 2 // 3
可以看見結(jié)果,這個(gè)方法的流程是什么樣的呢?
第一個(gè) then 函數(shù)和之前講的一樣,處理 promise 的 fulfilled,第二個(gè) then 的函數(shù)是處理前一個(gè) then 函數(shù)處理完的結(jié)果,他們之間參數(shù)傳遞的途徑是前一個(gè) then 函數(shù) return 一個(gè)數(shù)據(jù),然后后一個(gè) then 函數(shù)接受到這個(gè)參數(shù)。
如果你愿意,可以再后面加很多很多個(gè) then,他們的流程主要是這樣。
如果我們把 catch 提前呢?
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(1); }, 2000); }); }; testFoo().then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); return ++result; }).catch(function(error) { console.log(error); }).then(function(result) { console.log(result); return ++result; }); // 1 // 2 // 3
可以看出,結(jié)果一樣,說明catch只會在發(fā)生 reject 的時(shí)候調(diào)用。
那如果在中間的一個(gè) then 中拋出一個(gè)異常呢?
var testFoo = function() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(1); }, 2000); }); }; testFoo().then(function(result) { console.log(result); return ++result; }).then(function(result) { console.log(result); throw new Error("throw Error") return ++result; }).then(function(result) { console.log(result); return ++result; }).catch(function(error) { console.log(error); }); // 1 // 2 // Error: throw Error
我們在第二個(gè)then中拋出一個(gè)異常,而后立即被 catch 捕捉到,第三個(gè) then 并沒有執(zhí)行。
到這里我們想一下從then的傳參和捕捉異常來看,新加一個(gè) then 只是注冊了一個(gè)回調(diào)函數(shù)那么簡單嗎?
不不不,每次調(diào)用then都會返回一個(gè)新創(chuàng)建的promise對象,這就解釋了上面的一切原因。
試想一個(gè)場景,我們執(zhí)行多個(gè)異步操作(ajax等等),但是我們想在這幾個(gè)操作都完成的時(shí)候才去執(zhí)行一個(gè)函數(shù)。
如果按照傳統(tǒng)的方法來做,代碼會很亂很散,關(guān)鍵不優(yōu)雅。讓我們嘗試用 promise 。
先看這個(gè) demo
var testFoo = function(time, value) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2); }, task2: function() { return testFoo(1.3, 3); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { function recordValue(results, value) { results.push(value); console.log(value); console.log(results); return results; } var pushValue = recordValue.bind(null, []); return tasks.task1().then(pushValue).then(tasks.task2).then(pushValue).then(tasks.task3).then(pushValue); }; main().then(function(value) { console.log(value); }); // [2, 3, 1]
這么實(shí)現(xiàn)明顯看起來凌亂,特別對于 main 函數(shù),可讀性也比較差。
那么有沒有更優(yōu)雅的方法呢,答案:有!?。
Promise.all 方法接受一個(gè)以 promise 對象為元素的數(shù)組,在全部執(zhí)行操作完成后才回調(diào)用then里面的方法,看代碼:
var testFoo = function(time, value) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2); }, task2: function() { return testFoo(1.3, 3); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { return Promise.all([tasks.task1(), tasks.task2(), tasks.task3()]); } main().then(function(result) { console.log(result); }); // [2, 3, 1]
我們吧要執(zhí)行的 promise 對象作為數(shù)組的元素傳給 Promise.all(),Promise.all().then()里面定義的函數(shù)接受到一個(gè)數(shù)組,元素是這幾個(gè)操作返回的值,順序和 promise 對象放入的順序一樣,比如第一個(gè) promise 對象返回的值是2,那么結(jié)果的第一個(gè)元素就是2。
并且每一個(gè)promise是同時(shí)執(zhí)行的--并發(fā)。
在所有 promise 的狀態(tài)為 FulFilled 的時(shí)候才會去執(zhí)行 then 里面的函數(shù)。
那么捕捉異常呢?
修改一下例子:
var testFoo = function(time, value, err) { return new Promise(function(resolve, reject) { setTimeout(function() { if(err) { reject(err); return false; } resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2, "error"); }, task2: function() { return testFoo(1.3, 3, "error1"); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { return Promise.all([tasks.task1(), tasks.task2(), tasks.task3()]); } main().then(function(result) { console.log(result); }).catch(function(err) { console.log(err); }); // [2, 3, 1]
我們讓其中2 promise 個(gè)拋出異常,看到捕捉到了那個(gè)異常,而且是捕捉到第一個(gè)異常就停止了。
說明 all 的操作是當(dāng)所有 promise 狀態(tài)為 FulFilled 的時(shí)候才會執(zhí)行 then 的操作。而一旦有一個(gè) Rejected 就catch這個(gè)異常,并且停止。
他和 Promise.all 一樣,接受一個(gè) promise 對象組成的數(shù)組,也是并發(fā)執(zhí)行,但是 Promise.race 是只要有一個(gè)promise對象進(jìn)入 FulFilled 或者 Rejected 狀態(tài)的話,就會繼續(xù)進(jìn)行后面的處理。
var testFoo = function(time, value) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(value); }, time * 1000); }); }; var tasks = { task1: function() { return testFoo(1, 2); }, task2: function() { return testFoo(1.3, 3); }, task3: function() { return testFoo(1.5, 1); } }; var main = function() { return Promise.race([tasks.task1(), tasks.task2(), tasks.task3()]); } main().then(function(result) { console.log(result); }); // 2
可以看到,task1 最先完成,然后就拿到他的值進(jìn)行 then 操作。
原文來自我的博客 http://qiutc.me/post/promise-learn-note.html
歡迎大家關(guān)注~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/87729.html
摘要:是一個(gè)注冊在指定源和路徑下的事件驅(qū)動(dòng)。可以提供有效有效的離線體驗(yàn),攔截網(wǎng)絡(luò)請求。出于安全原因,要求必須在下才能運(yùn)行。返回一個(gè)對象,的結(jié)果是對象值對象組成的數(shù)組。當(dāng)事件的處理程序執(zhí)行完畢后,可以認(rèn)為安裝完成了。 在前端越來越重的這個(gè)時(shí)代,頁面加載速度成為了一個(gè)重要的指標(biāo)。對于這個(gè)問題,業(yè)界也有一些解決方案。 瀏覽器緩存、協(xié)議緩存、強(qiáng)緩存 懶加載(首屏) CDN 多域名突破下載并發(fā)限制。...
摘要:是一個(gè)注冊在指定源和路徑下的事件驅(qū)動(dòng)。可以提供有效有效的離線體驗(yàn),攔截網(wǎng)絡(luò)請求。出于安全原因,要求必須在下才能運(yùn)行。返回一個(gè)對象,的結(jié)果是對象值對象組成的數(shù)組。當(dāng)事件的處理程序執(zhí)行完畢后,可以認(rèn)為安裝完成了。 在前端越來越重的這個(gè)時(shí)代,頁面加載速度成為了一個(gè)重要的指標(biāo)。對于這個(gè)問題,業(yè)界也有一些解決方案。 瀏覽器緩存、協(xié)議緩存、強(qiáng)緩存 懶加載(首屏) CDN 多域名突破下載并發(fā)限制。...
摘要:把回調(diào)函數(shù)寫法分離出來,在異步操作執(zhí)行完后,用鏈?zhǔn)秸{(diào)用的方法執(zhí)行回調(diào)函數(shù),對于多層回調(diào)來說,非常的方便,可以繼續(xù)在的方法中繼續(xù)寫對象并返回,繼續(xù)調(diào)用來進(jìn)行回調(diào)操作,這就是的作用。 Promise是什么 JS就是操作對象上的屬性和方法,對于一個(gè)對象,想要了解,我們可以直接從其身上的屬性和方法入手;直接使用console.dir(對象)打印出來 showImg(https://segm...
摘要:回調(diào)函數(shù)是的一大特色官方的基本都是以會回調(diào)方式傳遞函數(shù)返回值。針對這種普遍問題,應(yīng)勢而生基本用法創(chuàng)建做一些異步操作的事情,然后一切正常的構(gòu)造器接受一個(gè)函數(shù)作為參數(shù),它會傳遞給這個(gè)回調(diào)函數(shù)兩個(gè)變量和。 Promise 是什么? Promise 對象用來進(jìn)行延遲(deferred) 和 異步(asynchronous) 計(jì)算。 一個(gè) Promise 處于以下三種狀態(tài)之一: pend...
摘要:前言筆者的前端開發(fā)已經(jīng)有些時(shí)日了,對于一直保留著最初的恐懼,倘若一座不可跨越的高山,思前想后終于邁出最后一步,踏入了開拓自己視野的新視界,希望在看這篇文章的你可以一起跟我動(dòng)手嘗試。面向的下一代框架。由團(tuán)隊(duì)打造,特點(diǎn)優(yōu)雅簡潔靈活體積小。 showImg(https://segmentfault.com/img/bVbuorM?w=1514&h=568); 前言 ?????筆者的前端開發(fā)已...
閱讀 2306·2021-11-25 09:43
閱讀 2942·2019-08-30 15:52
閱讀 1893·2019-08-30 15:44
閱讀 982·2019-08-30 10:58
閱讀 762·2019-08-29 18:43
閱讀 3217·2019-08-29 18:36
閱讀 2316·2019-08-29 17:02
閱讀 1458·2019-08-29 17:01