JS異步編程方法
setTimeout(callback, 1000)
- 回調形式容易形成回調地獄,故es6發明了promise,採用鏈式回調的方式
const test = new Promise();
test.then(cb1).then(cb2);
- 鏈式回調雖然更加直觀了,但仍是要將函數經過then傳遞,更爲理想的方式是,用同步的寫法去寫異步邏輯,因而es7給出了async和await關鍵字
const p1 = () => { return new Promise((res) => {
setTimout(() => {
console.log('p1');
res(2);
}, 1000);
})};
const p = async () => {
const p2 = await p1();
console.log(p2);
}
// 輸出 p1 2
- 因而可知,async和await處理異步是基於promise的,await後面須要跟一個promise(一般是一個返回promise的函數,跟在await後面並執行),若是不跟promise也不會報錯,會視做普通同步函數執行。
- async/await是語法糖,用async標記的函數,在其內部遇到await標記的邏輯時,會暫時返回,不執行後續的邏輯,等await內部的邏輯處理完畢後,再繼續走await後面的邏輯,這個方式,其實就是es6定義的generator函數。即async與await將標記的函數轉換成了生成器。
原理
- 將p變成一個generator函數,其中遇到await的地方就改寫成yield:
function* p () {
const p2 = yield p1();
console.log(p2)
}
- 咱們能夠運行p(),獲得一個迭代器,調用迭代器的next(),執行下一步,但這種方式須要手動調用next纔會繼續執行函數p,因此咱們就須要一個自動執行這個函數的函數asyncFunc:
asyncFunc(generator) {
const gen = generator();
function next(data) {
const { value, done } = gen.next(data);
if (done) {
return value;
} else if (!(value instanceof Promise)) {
next(value);
} else {
value.then((data) => next(data));
}
}
next();
}
asyncFunc(p);
- 經過調用asyncFunc(p),咱們執行了生成器p,獲得迭代器gen,經過遞歸next方法,將gen自動執行到底(即done = true時,每次調用迭代器的next,都會返回value和done標誌,value是yield後面表達式的值);
- 而當yield後面表達式返回一個promise時,經過將迭代器的next方法放到pormise的then中執行,使得yield後面的邏輯要等待p1完成後才繼續進行,即達到同步的效果
- 可見async/await是經過將函數變爲一個生成器函數,並使用自動執行函數來執行他,在執行過程當中,有意地讓生成的迭代器放到promise的then中,即異步完成後才執行,從而達到的同步效果。
- 完整流程以下:
const p1 = () => { return new Promise((res) => {
setTimout(() => {
console.log('p1');
res(2);
}, 1000);
})};
function* p () {
const p2 = yield p1();
console.log(p2)
}
asyncFunc(generator) {
const gen = generator();
function next(data) {
const { value, done } = gen.next(data);
if (done) {
return value;
} else if (!(value instanceof Promise)) {
next(value);
} else {
value.then((data) => next(data));
}
}
next();
}
asyncFunc(p);
- 首先自動執行函數asyncFunc執行生成器p獲得迭代器gen
- 調用next函數,執行gen.next,這時迭代器執行到p函數的yield p1(),返回一個promise,該promise一秒後將打印'p1',並返回2
- 在此以前,自執行函數,在該promise的回調中傳入了next方法
- 則一秒後,打印'p1',返回2,執行next(2)
- 則迭代器執行yield p1()後面的邏輯,而且,把2賦給了p2(generator語法如此)
- 打印'2'
- async/await的實現要比上述的複雜,但核心邏輯就是generator結合自執行函數。