目錄
- 前言
- 一、Promise的原理與基本語法
- 1.Promise的原理
- 2.Promise的基本語法
- 二、Promise多個串聯操作
- 三、Promise常用方法
- 四、Async/Await簡介與用法
- 1、Async/Await簡介
- 2、Async/Await的用法
- 五、Async/Await錯誤處理
- 六、為什么Async/Await更好?
- 1. 簡潔
- 2. 中間值
- 3.條件陳述句
前言
異步編程模式在前端開發程序中,顯得越來越重要,從最開始的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實體
<script src="https://cdn.bootcss.com/bluebird/3.5.1/bluebird.min.js"></script>//如果低版本瀏覽器不支持Promise,通過cdn這種方式
<script>
function loadImg(src) {
var promise = new Promise(function(resolve, reject) {
var img = document.createElement('img')
img.onload = function() {
resolve(img)
}
img.onerror = function() {
reject('圖片加載失敗')
}
img.src = https://www.cnblogs.com/xzsj/p/src
})
return promise
}
var src ='https://img.uj5u.com/2021/01/19/216792190831392.png'
var result = loadImg(src)
result.then(
function(img) {
console.log(1, img.width)
return img
},
function() {
console.log('error 1')
}
).then(function(img) {
console.log(2, img.height)
})
</script>
二、Promise多個串聯操作
Promise還可以做更多的事情,比如,有若干個異步任務,需要先做任務1,如果成功后再做任務2,任何任務失敗則不再繼續并執行錯誤處理函式,要串行執行這樣的異步任務,不用Promise需要寫一層一層的嵌套代碼,
有了Promise,我們只需要簡單地寫job1.then(job2).then(job3).catch(handleError);
其中job1、job2和job3都是Promise物件,
比如我們想實作第一個圖片加載完成后,再加載第二個圖片,如果其中有一個執行失敗,就執行錯誤函式:
var src1 = 'https://img.uj5u.com/2021/01/19/216792190831392.png'
var result1 = loadImg(src1) //result1是Promise物件
var src2 = 'https://img.uj5u.com/2021/01/19/216792190831393.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://img.uj5u.com/2021/01/19/216792190831392.png'
var result1 = loadImg(src1)
var src2 = 'https://img.uj5u.com/2021/01/19/216792190831393.jpg'
var result2 = loadImg(src2)
Promise.all([result1, result2]).then(function(datas) {
console.log('all', datas[0]) //<img src="https://img.uj5u.com/2021/01/19/216792190831392.png">
console.log('all', datas[1]) //<img src="https://img.uj5u.com/2021/01/19/216792190831393.jpg">
})
Promise.race([result1, result2]).then(function(data) {
console.log('race', data) //<img src="https://img.uj5u.com/2021/01/19/216792190831393.jpg">
})
如果我們組合使用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 = https://www.cnblogs.com/xzsj/p/src
})
return promise
}
const src1 ='https://img.uj5u.com/2021/01/19/216792190831392.png'
const src2 = 'https://img.uj5u.com/2021/01/19/216792190831393.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 =https://www.cnblogs.com/xzsj/p/> {
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 = https://www.cnblogs.com/xzsj/p/await getJSON()
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData)
return moreData
} else {
console.log(data)
return data
}
}
作者:浪里行舟
鏈接:異步解決方案----Promise與Await
來源:github
著作權歸作者所有,商業轉載請聯系作者獲得授權,非商業轉載請注明出處,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/250546.html
標籤:JavaScript
