摘要:前言假如需要向后端發(fā)送一個(gè)請求,并對返回的數(shù)據(jù)進(jìn)行操作,可能我們第一時(shí)間想到的是回調(diào)函數(shù)。如果值本身就是一個(gè)對象,則替代默認(rèn)的對象作為返回值如果值為其他值,則將這個(gè)值作為返回的的函數(shù)的參數(shù)值。
前言
假如需要向后端發(fā)送一個(gè)請求,并對返回的數(shù)據(jù)進(jìn)行操作,可能我們第一時(shí)間想到的是回調(diào)函數(shù)。但如果接著又需要執(zhí)行第二個(gè)、第三個(gè)...第n個(gè)異步操作,那么回調(diào)函數(shù)就會(huì)一層層的嵌套,嚴(yán)重影響了代碼可讀性和可維護(hù)性。
Promise就是解決這個(gè)問題的方案,Promise主要做的事情是把回調(diào)函數(shù)的嵌套邏輯替換成了符合正常人思維習(xí)慣的線性邏輯,本文主要介紹Promise的基本用法、API、鏈?zhǔn)讲僮鳌惓L幚硪约袄胮romise對數(shù)組進(jìn)行異步操作的方法。
一、雜說Promise是從DOM中的Futures引入javascript的,理由大概是出現(xiàn)了像NodeJs這樣獨(dú)立于瀏覽器之外的JavaScript運(yùn)行環(huán)境。在Promise正式被實(shí)現(xiàn)之前,部分JS庫,像Q、when、WinJS、RSVP.js、jQuery都根據(jù)Promises/A+標(biāo)準(zhǔn)分別實(shí)現(xiàn)了略有差異的”類Promise“對象。如果你的項(xiàng)目中用到了這些庫,不用擔(dān)心,標(biāo)準(zhǔn)的Promise對象提供了將這些”類Promise“對象轉(zhuǎn)換為標(biāo)準(zhǔn)Promise對象的方法(后文會(huì)提到)。關(guān)于Promise的兼容性,參考Can I Use。
Promise有三種狀態(tài):pending、resolved、rejected,狀態(tài)之間的轉(zhuǎn)換只能從pending到resolved或rejected,并且狀態(tài)一旦轉(zhuǎn)換就再也無法改變;
Promise的API:
Promise的構(gòu)造器接受一個(gè)函數(shù),這個(gè)函數(shù)接受兩個(gè)參數(shù):resolved,rejected。
promise.then(onResolved, onRejected), 不做贅述;
promise.catch(onRejected), promise.then(undefined, onRejected)的語法糖。
Promise.resolve(argument),返回一個(gè)Promise對象,具體取決于它接受的參數(shù)類型。
參數(shù)為一個(gè)Promise對象,直接返回這個(gè)對象;
參數(shù)為一個(gè)“類promise”對象,將其轉(zhuǎn)化成真正的Promise對象并返回;
參數(shù)為其他值,返回一個(gè)以參數(shù)值作為其resolved函數(shù)參數(shù)的Promise對象;
Promise.reject(obj), 返回一個(gè)以參數(shù)值(Error的實(shí)例)作為其reject函數(shù)參數(shù)的Promise對象;
Promise.all(array), 參數(shù)值為Promise數(shù)組(也可以包含"類Promise"對象),對數(shù)組的每一項(xiàng)調(diào)用Promise.resolve(),全部成功則resolved并返回返回值的數(shù)組,否則返回第一個(gè)rejected的error對象;
Promise.race(array), 返回?cái)?shù)組中最先resolved或者rejected的那個(gè)Promise對象的返回值或者error對象。
二、基本用法Promise是一個(gè)JavaScript對象,它執(zhí)行在未來的某個(gè)時(shí)刻才知道結(jié)果的操作并返回得到的值或者失敗的信息。
// Promise is something like this. var promise = new Promise(function(resolved, rejected) { doSomethingAsync(); if (success) { resolved(); } else { rejected(); } }) //How to use a promise. First arg is resolved, second is rejected promise.then(function(res) { console.log(res); }, function(err) { alert(err); })三、鏈?zhǔn)秸{(diào)用
如果僅有一個(gè)Promise對象的話,情況較為簡單,即在Promise對象被定義時(shí)異步操作就開始執(zhí)行,我們關(guān)心的并不是它什么時(shí)候執(zhí)行完畢,而是要在它執(zhí)行完或者返回錯(cuò)誤后對結(jié)果進(jìn)行處理。但當(dāng)多個(gè)Promise要按照一定的順序執(zhí)行時(shí),事情就變得復(fù)雜起來了。
function fetchSomething() { return new Promise(function(resolved) { if (success) { resolved(res); } }); } fetchSomething().then(function(res) { console.log(res); return fetchSomething(); }).then(function(res) { console.log("duplicate res"); return "done"; }).then(function(tip) { console.log(tip); })
then函數(shù)始終返回一個(gè)promise對象,后續(xù)的then要等待返回的promise resolve后才能執(zhí)行,這樣就實(shí)現(xiàn)了線性邏輯的鏈?zhǔn)秸{(diào)用。而返回的promise取決于then函數(shù)本身return的值。如果return值本身就是一個(gè)promise對象,則替代默認(rèn)的promise對象作為返回值;如果return值為其他值,則將這個(gè)值作為返回的promise的resolve函數(shù)的參數(shù)值。
四、異常處理從上面的代碼可以看出,then函數(shù)接受兩個(gè)參數(shù):resolved、rejected。上面沒寫rejected是因?yàn)閞ejected函數(shù)是可選的,當(dāng)然也可以在then之后寫catch,.catch(rejected)本質(zhì)上是.then(undefined, rejected)的語法糖。
這兩種方式是有區(qū)別的,.then(resolved, rejected)只能捕獲之前的promise的異常,而寫在其后的.catch(undefined, rejected)還可以捕獲其resolved函數(shù)產(chǎn)生的異常。另外只要Promise鏈中有一個(gè)promise對象拋出異常,其后所有的resolved都被跳過,直到這個(gè)異常被rejected或者catch處理。
當(dāng)需要用數(shù)組的數(shù)據(jù)執(zhí)行異步操作,因?yàn)閿?shù)組的遍歷方法forEach、map等都是同步的,所以結(jié)果的順序就取決于異步操作完成的順序,如果對順序有要求,這樣就不盡人意。
// 假設(shè)fetchID返回一個(gè)Promise對象 names.forEach(function(name) { fetchID(name).then(function(id) { renderInfo(id); }) })
這個(gè)時(shí)候就需要利用then()來制定順序:
names.reduce(function(sequence, name) { return sequence.then(function() { return fetchID(name); }).then(function(id) { renderID(id); }) }, Promise.then())
因?yàn)榇藭r(shí)先遍歷的name處理的結(jié)果將作為后面的sequence,構(gòu)成了鏈?zhǔn)疥P(guān)系,就避免了下載速度決定順序的問題。但仍然可以優(yōu)化:因?yàn)榇藭r(shí)的ID是獲取一個(gè),render一個(gè)的。如果能夠先獲取所有的ID再逐條渲染的話,性能會(huì)更好。
Promise.all(names.map(fetchID)) .then(function(IDs) { IDS.forEach(function(id) { renderID(id); //同步 }) })
參考文章:
JavaScript Promises: an Introduction (自備梯子)
Master the JavaScript Interview: What is a Promise? (自備梯子)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87313.html
摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。怎么處理每個(gè)引擎都有一個(gè)基本組件,稱為調(diào)用棧。也就是說,如果有其他函數(shù)等待執(zhí)行,函數(shù)是不能離開調(diào)用棧的。每個(gè)異步函數(shù)在被送入調(diào)用棧之前必須通過回調(diào)隊(duì)列。例如方法是在中傳遞的回調(diào)函數(shù)。 ? 翻譯:瘋狂的技術(shù)宅 原文:www.valentinog.com/blog/engine… 從Call Stack,Global Me...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...
摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。單線程的我們說是單線程的,因?yàn)橛幸粋€(gè)調(diào)用棧處理我們的函數(shù)。也就是說,如果有其他函數(shù)等待執(zhí)行,函數(shù)是不能離開調(diào)用棧的。每個(gè)異步函數(shù)在被送入調(diào)用棧之前必須通過回調(diào)隊(duì)列。 翻譯:瘋狂的技術(shù)宅原文:https://www.valentinog.com/bl... 本文首發(fā)微信公眾號:前端先鋒歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 sh...
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:為這些回調(diào)函數(shù)分別命名并分離存放可以在形式上減少嵌套,使代碼清晰,但仍然不能解決問題。如果在一個(gè)結(jié)束成功或失敗,同前面的說明后,添加針對成功或失敗的回調(diào),則回調(diào)函數(shù)會(huì)立即執(zhí)行。 異步? 我在很多地方都看到過異步(Asynchronous)這個(gè)詞,但在我還不是很理解這個(gè)概念的時(shí)候,卻發(fā)現(xiàn)自己常常會(huì)被當(dāng)做已經(jīng)很清楚(* ̄? ̄)。 如果你也有類似的情況,沒關(guān)系,搜索一下這個(gè)詞,就可以得到大致...
摘要:的執(zhí)行與狀態(tài)無關(guān)當(dāng)?shù)玫綘顟B(tài)不論成功或失敗后就會(huì)執(zhí)行,原文鏈接參考鏈接對象 同期異步系列文章推薦談一談javascript異步j(luò)avascript異步中的回調(diào)javascript異步與promisejavascript異步之Promise.resolve()、Promise.reject()javascript異步之Promise then和catchjavascript異步之a(chǎn)sync...
閱讀 678·2021-11-15 11:37
閱讀 4135·2021-09-09 09:34
閱讀 3573·2019-08-30 15:52
閱讀 2613·2019-08-29 14:03
閱讀 2854·2019-08-26 13:36
閱讀 1597·2019-08-26 12:16
閱讀 1602·2019-08-26 11:45
閱讀 3494·2019-08-23 18:41