摘要:前言異步編程模式在前端開發(fā)過程中,顯得越來越重要。隨著新標(biāo)準(zhǔn)的到來,處理異步數(shù)據(jù)流又有了新的方案。接下來我們介紹這兩種處理異步編程的方案。仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄。使得異步代碼看起來像同步代碼,再也沒有回調(diào)函數(shù)。
前言
異步編程模式在前端開發(fā)過程中,顯得越來越重要。從最開始的XHR到封裝后的Ajax都在試圖解決異步編程過程中的問題。隨著ES6新標(biāo)準(zhǔn)的到來,處理異步數(shù)據(jù)流又有了新的方案。我們都知道,在傳統(tǒng)的ajax請(qǐng)求中,當(dāng)異步請(qǐng)求之間的數(shù)據(jù)存在依賴關(guān)系的時(shí)候,就可能產(chǎn)生很難看的多層回調(diào),俗稱"回調(diào)地獄"(callback hell),這卻讓人望而生畏,Promise的出現(xiàn)讓我們告別回調(diào)函數(shù),寫出更優(yōu)雅的異步代碼。在實(shí)踐過程中,卻發(fā)現(xiàn)Promise并不完美,Async/Await是近年來JavaScript添加的最革命性的的特性之一,Async/Await提供了一種使得異步代碼看起來像同步代碼的替代方法。接下來我們介紹這兩種處理異步編程的方案。
一、Promise的原理與基本語法 1.Promise的原理Promise 是一種對(duì)異步操作的封裝,可以通過獨(dú)立的接口添加在異步操作執(zhí)行成功、失敗時(shí)執(zhí)行的方法。主流的規(guī)范是 Promises/A+。
Promise中有幾個(gè)狀態(tài):
pending: 初始狀態(tài), 非 fulfilled 或 rejected;
fulfilled: 成功的操作,為表述方便,fulfilled 使用 resolved 代替;
rejected: 失敗的操作。
pending可以轉(zhuǎn)化為fulfilled或rejected并且只能轉(zhuǎn)化一次,也就是說如果pending轉(zhuǎn)化到fulfilled狀態(tài),那么就不能再轉(zhuǎn)化到rejected。并且fulfilled和rejected狀態(tài)只能由pending轉(zhuǎn)化而來,兩者之間不能互相轉(zhuǎn)換。
2.Promise的基本語法Promise實(shí)例必須實(shí)現(xiàn)then這個(gè)方法
then()必須可以接收兩個(gè)函數(shù)作為參數(shù)
then()返回的必須是一個(gè)Promise實(shí)例
//如果低版本瀏覽器不支持Promise,通過cdn這種方式二、Promise多個(gè)串聯(lián)操作
Promise還可以做更多的事情,比如,有若干個(gè)異步任務(wù),需要先做任務(wù)1,如果成功后再做任務(wù)2,任何任務(wù)失敗則不再繼續(xù)并執(zhí)行錯(cuò)誤處理函數(shù)。要串行執(zhí)行這樣的異步任務(wù),不用Promise需要寫一層一層的嵌套代碼。
有了Promise,我們只需要簡(jiǎn)單地寫job1.then(job2).then(job3).catch(handleError);
其中job1、job2和job3都是Promise對(duì)象。
比如我們想實(shí)現(xiàn)第一個(gè)圖片加載完成后,再加載第二個(gè)圖片,如果其中有一個(gè)執(zhí)行失敗,就執(zhí)行錯(cuò)誤函數(shù):
var src1 = "https://www.imooc.com/static/img/index/logo_new.png" var result1 = loadImg(src1) //result1是Promise對(duì)象 var src2 = "https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg" var result2 = loadImg(src2) //result2是Promise對(duì)象 result1.then(function (img1) { console.log("第一個(gè)圖片加載完成", img1.width) return result2 // 鏈?zhǔn)讲僮? }).then(function (img2) { console.log("第二個(gè)圖片加載完成", img2.width) }).catch(function (ex) { console.log(ex) })
這里需注意的是:then 方法可以被同一個(gè) promise 調(diào)用多次,then 方法必須返回一個(gè) promise 對(duì)象。上例中result1.then如果沒有明文返回Promise實(shí)例,就默認(rèn)為本身Promise實(shí)例即result1,result1.then返回了result2實(shí)例,后面再執(zhí)行.then實(shí)際上執(zhí)行的是result2.then
三、Promise常用方法除了串行執(zhí)行若干異步任務(wù)外,Promise還可以并行執(zhí)行異步任務(wù)。
試想一個(gè)頁面聊天系統(tǒng),我們需要從兩個(gè)不同的URL分別獲得用戶的個(gè)人信息和好友列表,這兩個(gè)任務(wù)是可以并行執(zhí)行的,用Promise.all()實(shí)現(xiàn)如下:
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, "P1"); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, "P2"); }); // 同時(shí)執(zhí)行p1和p2,并在它們都完成后執(zhí)行then: Promise.all([p1, p2]).then(function (results) { console.log(results); // 獲得一個(gè)Array: ["P1", "P2"] });
有些時(shí)候,多個(gè)異步任務(wù)是為了容錯(cuò)。比如,同時(shí)向兩個(gè)URL讀取用戶的個(gè)人信息,只需要獲得先返回的結(jié)果即可。這種情況下,用Promise.race()實(shí)現(xiàn):
var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, "P1"); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, "P2"); }); Promise.race([p1, p2]).then(function (result) { console.log(result); // "P1" });
由于p1執(zhí)行較快,Promise的then()將獲得結(jié)果"P1"。p2仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄。
總結(jié):Promise.all接受一個(gè)promise對(duì)象的數(shù)組,待全部完成之后,統(tǒng)一執(zhí)行success;
Promise.race接受一個(gè)包含多個(gè)promise對(duì)象的數(shù)組,只要有一個(gè)完成,就執(zhí)行success
接下來我們對(duì)上面的例子做下修改,加深對(duì)這兩者的理解:
var src1 = "https://www.imooc.com/static/img/index/logo_new.png" var result1 = loadImg(src1) var src2 = "https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg" var result2 = loadImg(src2) Promise.all([result1, result2]).then(function (datas) { console.log("all", datas[0])// console.log("all", datas[1])// }) Promise.race([result1, result2]).then(function (data) { console.log("race", data)// })
如果我們組合使用Promise,就可以把很多異步任務(wù)以并行和串行的方式組合起來執(zhí)行
四、Async/Await簡(jiǎn)介與用法異步操作是 JavaScript 編程的麻煩事,很多人認(rèn)為async函數(shù)是異步操作的終極解決方案。
1、Async/Await簡(jiǎn)介async/await是寫異步代碼的新方式,優(yōu)于回調(diào)函數(shù)和Promise。
async/await是基于Promise實(shí)現(xiàn)的,它不能用于普通的回調(diào)函數(shù)。
async/await與Promise一樣,是非阻塞的。
async/await使得異步代碼看起來像同步代碼,再也沒有回調(diào)函數(shù)。但是改變不了JS單線程、異步的本質(zhì)。
2、Async/Await的用法使用await,函數(shù)必須用async標(biāo)識(shí)
await后面跟的是一個(gè)Promise實(shí)例
需要安裝babel-polyfill,安裝后記得引入 //npm i --save-dev babel-polyfill
function loadImg(src) { const promise = new Promise(function (resolve, reject) { const img = document.createElement("img") img.onload = function () { resolve(img) } img.onerror = function () { reject("圖片加載失敗") } img.src = src }) return promise } const src1 = "https://www.imooc.com/static/img/index/logo_new.png" const src2 = "https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg" const load = async function(){ const result1 = await loadImg(src1) console.log(result1) const result2 = await loadImg(src2) console.log(result2) } load()
當(dāng)函數(shù)執(zhí)行的時(shí)候,一旦遇到 await 就會(huì)先返回,等到觸發(fā)的異步操作完成,再接著執(zhí)行函數(shù)體內(nèi)后面的語句。
五、Async/Await錯(cuò)誤處理await 命令后面的 Promise 對(duì)象,運(yùn)行結(jié)果可能是 rejected,所以最好把 await 命令放在 try...catch 代碼塊中。try..catch錯(cuò)誤處理也比較符合我們平常編寫同步代碼時(shí)候處理的邏輯。
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } }六、為什么Async/Await更好?
Async/Await較Promise有諸多好處,以下介紹其中三種優(yōu)勢(shì):
1. 簡(jiǎn)潔使用Async/Await明顯節(jié)約了不少代碼。我們不需要寫.then,不需要寫匿名函數(shù)處理Promise的resolve值,也不需要定義多余的data變量,還避免了嵌套代碼。
2. 中間值你很可能遇到過這樣的場(chǎng)景,調(diào)用promise1,使用promise1返回的結(jié)果去調(diào)用promise2,然后使用兩者的結(jié)果去調(diào)用promise3。你的代碼很可能是這樣的:
const makeRequest = () => { return promise1() .then(value1 => { return promise2(value1) .then(value2 => { return promise3(value1, value2) }) }) }
使用async/await的話,代碼會(huì)變得異常簡(jiǎn)單和直觀
const makeRequest = async () => { const value1 = await promise1() const value2 = await promise2(value1) return promise3(value1, value2) }3.條件語句
下面示例中,需要獲取數(shù)據(jù),然后根據(jù)返回?cái)?shù)據(jù)決定是直接返回,還是繼續(xù)獲取更多的數(shù)據(jù)。
const makeRequest = () => { return getJSON() .then(data => { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData => { console.log(moreData) return moreData }) } else { console.log(data) return data } }) }
代碼嵌套(6層)可讀性較差,它們傳達(dá)的意思只是需要將最終結(jié)果傳遞到最外層的Promise。使用async/await編寫可以大大地提高可讀性:
const makeRequest = async () => { const data = await getJSON() if (data.needsAnotherRequest) { const moreData = await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } }
如果覺得文章對(duì)你有些許幫助,歡迎在我的GitHub博客點(diǎn)贊和關(guān)注,感激不盡!
參考文章Async/Await替代Promise的6個(gè)理由
前端的異步解決方案之Promise和Await/Async
廖雪峰的Javascript教程
[[譯] Promises/A+ 規(guī)范](http://www.ituring.com.cn/art...
async 函數(shù)的含義和用法
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/98343.html
摘要:前言異步編程模式在前端開發(fā)過程中,顯得越來越重要。隨著新標(biāo)準(zhǔn)的到來,處理異步數(shù)據(jù)流又有了新的方案。接下來我們介紹這兩種處理異步編程的方案。仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄。使得異步代碼看起來像同步代碼,再也沒有回調(diào)函數(shù)。 前言 異步編程模式在前端開發(fā)過程中,顯得越來越重要。從最開始的XHR到封裝后的Ajax都在試圖解決異步編程過程中的問題。隨著ES6新標(biāo)準(zhǔn)的到來,處理異步數(shù)據(jù)流又有了新...
摘要:前言異步編程模式在前端開發(fā)過程中,顯得越來越重要。隨著新標(biāo)準(zhǔn)的到來,處理異步數(shù)據(jù)流又有了新的方案。接下來我們介紹這兩種處理異步編程的方案。仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄。使得異步代碼看起來像同步代碼,再也沒有回調(diào)函數(shù)。 前言 異步編程模式在前端開發(fā)過程中,顯得越來越重要。從最開始的XHR到封裝后的Ajax都在試圖解決異步編程過程中的問題。隨著ES6新標(biāo)準(zhǔn)的到來,處理異步數(shù)據(jù)流又有了新...
摘要:的翻譯文檔由的維護(hù)很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細(xì)介紹promise的gitbook,看完再不會(huì)promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:控制臺(tái)將顯示回調(diào)地獄通常,回調(diào)只能由一個(gè)異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡(jiǎn)化異步編碼旅程異步編程是一項(xiàng)在中無法避免的挑戰(zhàn)。 JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化? 請(qǐng)思考以下代碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數(shù)語言都處理每...
摘要:控制臺(tái)將顯示回調(diào)地獄通常,回調(diào)只能由一個(gè)異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡(jiǎn)化異步編碼旅程異步編程是一項(xiàng)在中無法避免的挑戰(zhàn)。 JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化? 請(qǐng)思考以下代碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數(shù)語言都處理每...
閱讀 3296·2021-11-24 09:39
閱讀 2805·2021-10-12 10:20
閱讀 1907·2019-08-30 15:53
閱讀 3076·2019-08-30 14:14
閱讀 2600·2019-08-29 15:36
閱讀 1121·2019-08-29 14:11
閱讀 1956·2019-08-26 13:51
閱讀 3408·2019-08-26 13:23