♣ 異步處理方案
需求:
- 我們需要向伺服器發送網絡請求擷取資料,一共需要發送三次請求;
- 第二次的請求url依賴于第一次的結果;
- 第三次的請求url依賴于第二次的結果;
依次類推;
多次回調、Promise中then
點選檢視代碼
function requestData(url) {
// 異步請求的代碼會被放入到executor中
return new Promise((resolve, reject) => {
// 模拟網絡請求
setTimeout(() => {
// 拿到請求的結果
resolve(url)
}, 2000);
})
}
// 需求:
// 1> url: url -> res: url
// 2> url: res + "aaa" -> res: urlaaa
// 3> url: res + "bbb" => res: urlaaabbb
// 1.第一種方案: 多次回調
// 回調地獄
// requestData("url").then(res => {
// requestData(res + "aaa").then(res => {
// requestData(res + "bbb").then(res => {
// console.log(res)
// })
// })
// })
// 2.第二種方案: Promise中then的傳回值來解決
//requestData("url").then(res => {
// return requestData(res + "aaa")
// }).then(res => {
// return requestData(res + "bbb")
// }).then(res => {
// console.log(res)
// })
Promise + generator
但是上面的代碼其實看起來也是閱讀性比較差的,有沒有辦法可以繼續來對上面的代碼進行優化呢?第三種方案: Promise + generator實作
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iN4UjM5EWOmRmMjFTM1IDOxYzX2MjNwcTMzAzLcFTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
點選檢視代碼
function requestData(url) {
// 異步請求的代碼會被放入到executor中
return new Promise((resolve, reject) => {
// 模拟網絡請求
setTimeout(() => {
// 拿到請求的結果
resolve(url)
}, 2000);
})
}
//但是上面的代碼其實看起來也是閱讀性比較差的,有沒有辦法可以繼續來對上面的代碼進行優化呢?
// 3.第三種方案: Promise + generator實作
function* getData() {
const res1 = yield requestData("url")
const res2 = yield requestData(res1 + "aaa")
const res3 = yield requestData(res2 + "bbb")
const res4 = yield requestData(res3 + "ccc")
console.log(res4)
}
// 1> 手動執行生成器函數
const generator = getData()
generator.next().value.then(res => {
generator.next(res).value.then(res => {
generator.next(res).value.then(res => {
generator.next(res)
})
})
})
點選檢視代碼
function requestData(url) {
// 異步請求的代碼會被放入到executor中
return new Promise((resolve, reject) => {
// 模拟網絡請求
setTimeout(() => {
// 拿到請求的結果
resolve(url)
}, 1000);
})
}
function* getData() {
const res1 = yield requestData("url")
const res2 = yield requestData(res1 + "aaa")
const res3 = yield requestData(res2 + "bbb")
const res4 = yield requestData(res3 + "ccc")
console.log(res4)
}
// 2> 自己封裝了一個自動執行的函數
function execGenerator(genFn) {
//拿到生成器
const generator = genFn()
function exec(res) {
const result = generator.next(res)
if (result.done) {
return result.value
}
result.value.then(res => {
exec(res)
})
}
exec()
}
execGenerator(getData)
我們還可以使用TJ:第三方包co自動執行,const co = require('co');co(getData)
異步函數 async function
本質上是Promise + generator的文法糖點選檢視代碼
function requestData(url) {
// 異步請求的代碼會被放入到executor中
return new Promise((resolve, reject) => {
// 模拟網絡請求
setTimeout(() => {
// 拿到請求的結果
resolve(url)
}, 2000);
})
}
// 4.第四種方案: async/await
//*—> async yield->await
async function getData() {
const res1 = await requestData("url")
const res2 = await requestData(res1 + "aaa")
const res3 = await requestData(res2 + "bbb")
const res4 = await requestData(res3 + "ccc")
console.log(res4)
}
getData()
async關鍵字用于聲明一個異步函數, async是asynchronous單詞的縮寫,異步、非同步;sync是synchronous單詞的縮寫,同步、同時;
點選檢視代碼
async function foo1() {
}
const foo2 = async () => {
}
class Foo {
async bar() {
}
}
異步函數的内部代碼執行過程和普通的函數是一緻的,預設情況下也是會被同步執行。
異步函數有傳回值時,和普通函數會有差別:
- 情況一:異步函數也可以有傳回值,但是異步函數的傳回值會被包裹到Promise.resolve中;
- 情況二:如果我們的異步函數的傳回值是Promise,Promise.resolve的狀态會由Promise決定;
- 情況三:如果我們的異步函數的傳回值是一個對象并且實作了thenable,那麼會由對象的then方法來決定;
點選檢視代碼
async function foo() {
console.log("foo function start~")
console.log("中間代碼~")
console.log("foo function end~")
//預設return undefined;
// 1.傳回一個值
// 2.傳回thenable
// return {
// then: function(resolve, reject) {
// resolve("hahahah")
// }
// }
// 3.傳回Promise
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("hehehehe")
}, 2000)
})
}
// 異步函數的傳回值一定是一個Promise
const promise = foo()
//當内部return 會執行這個then ,這個then被執行會加入到微任務隊列中
promise.then(res => {
console.log("promise then function exec:", res)
})
點選檢視代碼
async function foo() {
console.log("foo function start~")
console.log("中間代碼~")
// 異步函數中的異常, 會被作為異步函數傳回的Promise的reject值的
throw new Error("error message")
console.log("foo function end~")
}
// 異步函數的傳回值一定是一個Promise
foo().catch(err => {
console.log("err:", err)
})
console.log("後續還有代碼~~~~~")
- 通常使用await是後面會跟上一個表達式,這個表達式會傳回一個Promise;
- 那麼await會等到Promise的狀态變成fulfilled狀态,之後繼續執行異步函數;
- 如果await後面是一個普通的值,那麼會直接傳回這個值;
- 如果await後面是一個thenable的對象,那麼會根據對象的then方法調用來決定後續的值;
- 如果await後面的表達式,傳回的Promise是reject的狀态,那麼會将這個reject結果直接作為函數的Promise的reject值;
點選檢視代碼
// 1.await跟上表達式
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(222)
reject(1111)
}, 2000);
})
}
// async function foo() {
// const res1 = await requestData()
// console.log("後面的代碼1", res1)
// console.log("後面的代碼2")
// console.log("後面的代碼3")
// const res2 = await requestData()
// console.log("res2後面的代碼", res2)
// }
// 2.跟上其他的值
// async function foo() {
// // const res1 = await 123
// // const res1 = await {
// // then: function(resolve, reject) {
// // resolve("abc")
// // }
// // }
// const res1 = await new Promise((resolve) => {
// resolve("why")
// })
// console.log("res1:", res1)
// }
// 3.reject值 async function foo(){}是個promise,是以foo().catch
async function foo() {
const res1 = await requestData()
console.log("res1:", res1)
}
foo().catch(err => {
console.log("err:", err)
})