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