在 js 異步請求數據時,通常,我們多采用回調函數的方式解決,但是,如果有多個回調函數嵌套時,代碼顯得很不優雅,維護成本也相應較高。 ES6 提供的 Promise 方法和 ES7 提供的 Async/Await 語法糖可以更好解決多層回調問題。
Promise 對象用於表示一個異步操作的最終狀態(完成或失敗),以及其返回的值。
await 操作符用於等待一個Promise 對象。它只能在異步函數 async function 中使用。
await 表達式會暫停當前 async function 的執行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled),其回調的resolve函數參數作為 await 表達式的值,繼續執行 async function。
一個ajax請求時
通常 使用 ajax 請求數據時,會
$.ajax({
url: 'data1.json',
type: 'GET',
success: function (res) {
console.log(res) // 請求成功,則得到結果res
},
error: function(err) {
console.log(err)
}
})
上面可以得到我們想要的結果 res ---> { "url": "data2.json" }
多個ajax請求時
但是 當得到的數據 res 需要用於另一個 ajax 請求時,則需要如下寫法:
$.ajax({
url: 'data1.json',
type: 'GET',
success: function (res) {
$.ajax({
url: res.url, // 將 第一個ajax請求成功得到的res 用於第二個ajax請求
type: 'GET',
success: function (res) {
$.ajax({
url: res.url, // 將第二個ajax請求成功得到的res 用於第三個ajax請求
type: 'GET',
success: function (res) {
console.log(res) // {url: "this is data3.json"}
},
error: function(err) {
console.log(err)
}
})
},
error: function(err) {
console.log(err)
}
})
},
error: function(err) {
console.log(err)
}
})
上面出現多個回調函數的嵌套,可讀性較差(雖然這種嵌套在平常的開發中少見,但是在node服務端開發時,還是很常見的)
優化方法
使用 promise 鏈式操作
如下,使用 Promise,進行鏈式操作,可以使上面的異步代碼看起來如同步般易讀,從回調地獄中解脫出來。。
function ajaxGet (url) {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
type: 'GET',
success: function (res) {
resolve(res);
},
error: function(err) {
reject('請求失敗');
}
})
})
};
ajaxGet('data1.json').then((d) => {
console.log(d); // {url: "data2.json"}
return ajaxGet(d.url);
}).then((d) => {
console.log(d); // {url: "data3.json"}
return ajaxGet(d.url);
}).then((d) => {
console.log(d); // {url: "this is data3.json"}
})
注意,promise 返回的仍然是 promise
下面兩種方式的等效的:
1. 直接使用 promise
function ajaxPromiseGet(url) {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
type: 'GET',
success: function (res) {
resolve(res)
},
error: function (err) {
reject('請求失敗')
}
})
})
}
ajaxPromiseGet(`/products/list/`).then(list => {
if (list) {
console.log(list)
}
})
2. 當需要先統一處理 promise 返回值時
function ajaxPromiseGet(url) {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
type: 'GET',
success: function (res) {
resolve(res)
},
error: function (err) {
reject('請求失敗')
}
})
})
}
// 先統一處理 promise
function handleList () {
let ajaxReault = ajaxPromiseGet(`/products/list/`)
return ajaxReault.then(list => {
return list.filter(item => item.status == 0)
})
}
handleList().then(list => {
if (list) {
console.log(list)
}
})
Async/await 方法
- async 表示這是一個async函數,即異步函數,await只能用在這個函數里面。
- await 表示在這里等待promise返回結果了,再繼續執行。
- await 后面跟着的應該是一個promise對象(當然,其他返回值也沒關系,只是會立即執行,不過那樣就沒有意義了…)
- await 操作符用於等待一個Promise 對象。它只能在異步函數 async function 中使用。
- await 等待的雖然是promise對象,但不必寫.then(..),直接可以得到返回值。
執行一個ajax請求,可以通過如下方法:
function ajaxGet (url) {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
type: 'GET',
success: function (res) {
resolve(res)
},
error: function(err) {
reject('請求失敗')
}
})
})
};
async function getDate() {
console.log('開始')
let result1 = await ajaxGet('data1.json');
console.log('result1 ---> ', result1); // result1 ---> {url: "data2.json"}
};
getDate(); // 需要執行異步函數
執行多個ajax請求時:
function ajaxGet (url) {
return new Promise(function (resolve, reject) {
$.ajax({
url: url,
type: 'GET',
success: function (res) {
resolve(res)
},
error: function(err) {
reject('請求失敗')
}
})
})
};
async function getDate() {
console.log('開始')
let result1 = await ajaxGet('data1.json');
let result2 = await ajaxGet(result1.url);
let result3 = await ajaxGet(result2.url);
console.log('result1 ---> ', result1); // result1 ---> {url: "data2.json"}
console.log('result2 ---> ', result2); // result2 ---> {url: "data3.json"}
console.log('result3 ---> ', result3); // result3 ---> {url: "this is data3.json"}
};
getDate(); // 需要執行異步函數
async await捕捉錯誤:
- async await中.then(..)不用寫了,那么.catch(..)也不用寫,可以直接用標准的try catch語法捕捉錯誤。
例如,如果下面的 url 寫錯了
function ajaxGet (url) {
return new Promise(function (resolve, reject) {
$.ajax({
url: url111, // 此處為錯誤的 url
type: 'GET',
success: function (res) {
resolve(res)
},
error: function(err) {
reject('請求失敗')
}
})
})
};
async function getDate() {
console.log('開始')
try {
let result1 = await ajaxGet('data1.json'); // 執行到這里報錯,直接跳至下面 catch() 語句
let result2 = await ajaxGet(result1.url);
let result3 = await ajaxGet(result2.url);
console.log('result1 ---> ', result1);
console.log('result2 ---> ', result2);
console.log('result3 ---> ', result3);
} catch(err) {
console.log(err) // ReferenceError: url111 is not defined
}
};
getDate(); // 需要執行異步函數
源碼
更多
請參考:
- https://www.cnblogs.com/cckui/p/7809813.html
- https://www.cnblogs.com/aspwebchh/p/6629333.html
- https://cnodejs.org/topic/5640b80d3a6aa72c5e0030b6
- http://es6.ruanyifeng.com/#docs/async
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise