摘要:當迭代器運行后,會返回第一次運行到或者時的返回值以格式進行返回。而現在了后面的方法必須是總結總結一下異步代碼的發展過程回調函數最基本的解決方法,將異步結束函數以參數的方式傳遞到異步函數中,也就是使用回調函數的方式來實現異步邏輯。
介紹
寫過JS代碼的同學應該都知道,JS是單線程的,當出現異步邏輯時,就需要使用一些技巧來實現。最常見的方法就是使用回調方法。
回調方法比如我們要實現一個功能:1s后運行邏輯,再過3s運行另外一段邏輯。使用回調方法可以這樣寫:
// 方法一 ,嵌套回調 // 模擬異步邏輯 function delay(time, callback) { setTimeout(function() { callback(time); }, time); } // 過1000ms后輸出日志,再過3000ms后輸出日志。 delay(1000, function(time1) { console.log("%s ms后運行", time1); delay(3000, function(time2) { console.log("%s ms后運行", time2); }); });
運行上面的代碼,可以得到我們想要的結果:1s后輸出了日志,再過3s又輸出日志。但是如果邏輯復雜下去,會出現很深的回調方法嵌套問題,使得代碼不可維護。為了使異步代碼更清晰,就出現了Promise。
Promise還是拿上面的例子來實現:
// 方法二 promise function sleep(time) { return function() { return new Promise((resolve) => setTimeout(resolve, time)); }; } sleep(1000)().then(function() { console.log("1000ms后運行"); }).then(sleep(3000)).then(function() { console.log("3000ms后運行"); });
運行代碼后,還是可以得到和使用回調方法實現時一樣的效果。分析Promise實現的代碼,可以發現,它對嵌套回調進行了改進,將原先橫向發展的代碼,改成了縱向發展,但是并沒有解決本質問題。使用Promise的代碼,異步邏輯變成了一堆then方法,語義還是不夠清晰。那么有沒有更好的寫法呢?
在ES6中,提出了generator方法,可以做到使用同步代碼來實現異步功能。下面我們來重點介紹下generator。
generator是ES6中新提出的函數類型,最大的特點就是函數的執行可以被控制。
舉一個最簡單的generator例子:
function *idMarker() { var i = 1; while(true) { yield i++; } } var ids = idMarker(); ids.next().value; // 1 ids.next().value; // 2 ids.next().value; // 3
上面的例子中,當運行了generator函數idMarker()后,函數體并沒有開始運行,而是直接返回了一個迭代器ids。當迭代器運行next()后,會返回第一次運行到yield或者return時的返回值以格式{value:1,done:false}進行返回。其中value就是yield后面的值,done表示當前迭代器還沒有結束迭代。從上面的代碼可以看出,generator函數的運行過程可以在外邊被控制,也就是說使用generator可以做到以下功能的實現:
運行A邏輯; 通過yield語句,暫停A,并開始異步邏輯B; 等B運行完成,再繼續運行A;generator實現異步邏輯
從上面的例子可以看出,使用generator可以解決Promise的問題,代碼如下:
// 方法三 generator function sleep(time) { return new Promise((resolve) => setTimeout(function() { resolve(time); }, time)); } function run(gen) { return new Promise(function(resolve, reject) { gen = gen(); onFulfilled(); function onFulfilled(res) { var ret = gen.next(res); next(ret); } function next(ret) { if (ret.done) return resolve(ret.value); ret.value.then(onFulfilled); } }); } function *syncFn() { var d1 = yield sleep(1000); console.log("%s ms后運行", d1); var d2 = yield sleep(3000); console.log("%s ms后運行", d2); } run(syncFn);
下面我們來分析下上面代碼的邏輯:
首先運行run(syncFn);, 會運行到gen = gen();。這個時候gen變成了generator的迭代器。
通過運行onFulfilled中的gen.next(res),代碼開始運行syncFn中的sleep(1000)。此時,gen.next(res)會返回syncFn中yield獲得的值,即sleep方法返回的promise對象,并在next方法中對該promise設置了then(onFulfilled)。 也就是說,當sleep(1000)返回的promise運行結束后,會運行then中的onFulfilled。而onFulfilled會繼續運行syncFn的迭代器。這樣子,雖然syncFn中的異步邏輯,就會逐步執行了。
co是一個使用generator和yield來解決異步嵌套的工具庫。它的實現類似于上面例子中的run方法。向co傳入一個generator方法后,就會開始逐步執行其中的異步邏輯。
舉個例子,我們需要讀取三個文件并將文件內容依次打印出來。使用co的寫法就是:
var fs = require("fs"); var co = require("co"); function readFile(path) { return function (cb) { fs.readFile(path, {encoding: "utf8"}, cb); }; } co(function* () { var dataA = yield readFile("a.js"); console.log(dataA); var dataB = yield readFile("b.js"); console.log(dataB); var dataC = yield readFile("c.js"); console.log(dataC); }).catch(function (err) { console.log(err); });yield 與 yield*
yield語句還可以使用yield*,兩則存在細微的差別。舉個例子:
// 數組 function* GenFunc() { yield [1, 2]; yield* [3, 4]; yield "56"; yield* "78"; } var gen = GenFunc(); console.log(gen.next().value); // [1, 2] console.log(gen.next().value); // 3 console.log(gen.next().value); // 4 // generator函數 function* Gen1() { yield 2; yield 3; } function* Gen2() { yield 1; yield* Gen1(); yield 4; } var g2 = Gen2(); console.log(g2.next().value); // 1 console.log(g2.next().value); // 2 console.log(g2.next().value); // 3 console.log(g2.next().value); // 4 // 對象 function* GenFunc() { yield {a: "1", b: "2"}; yield* {a: "1", b: "2"}; } var gen = GenFunc(); console.log(gen.next()); // { value: { a: "1", b: "2" }, done: false } console.log(gen.next()); // TypeError: undefined is not a function
從上面幾個例子可以看出,yield 與 yield 的區別在于:yield 只是返回右側對象的值,而 yield 則將函數委托(delegate)到另一個生成器( Generator)或可迭代的對象(如字符串、數組和類數組 arguments,以及 ES6 中的 Map、Set 等)。也就是說,yield*會逐個調用右側可迭代對象的next方法。
async await上面講完了如何使用generator來解決異步代碼的問題,可以看出,使用generator后,異步邏輯的代碼基本和同步邏輯的代碼差不多了。但是,generator的運行需要co來支持,所有最后又出現了async和await。async和await其實就是generator的語法糖。舉個例子:
// generator function *syncFn() { var d1 = yield sleep(1000); console.log("%s ms后運行", d1); var d2 = yield sleep(3000); console.log("%s ms后運行", d2); }
上面的代碼使用async改寫就是:
// async function async syncFn() { var d1 = await sleep(1000); console.log("%s ms后運行", d1); var d2 = await sleep(3000); console.log("%s ms后運行", d2); }
可以看出,async的語法其實就是將generator中的*替換成async,將yield替換成await。
async和await的優勢有了generator,為什么還要提出async呢?因為async有以下幾點優勢:
async的語義更清晰
async方法自帶執行器,運行時和普通方法一樣。而generator的運行依賴于co
await后面的方法可以是任意方法。而co現在了yield后面的方法必須是Promise
總結總結一下異步代碼的發展過程:
【回調函數】最基本的解決方法,將異步結束函數以參數的方式傳遞到異步函數中,也就是使用回調函數的方式來實現異步邏輯。
【Promise】為了解決回調函數的橫向發展問題,定義了Promise。
【generator】Promise雖然解決了異步代碼橫向發展問題,可是使用Promise語義不夠清晰,代碼會呈現縱向發展趨勢,所以,ES6中出現了generator函數來解決異步代碼問題。
【async】generator函數基本上解決了異步代碼問題,但是generator函數的運行卻被外部控制著。最后提出了async,實現了generator + co的功能,而且語義更加清晰。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87890.html
摘要:定期進行負載測試負載測試顯示您的網站在一定數量的用戶訪問時的表現。如果負載測試顯示的頁面加載時間比預期的要長,那么網站設計的小改動就能帶來所需的改進。 確保網站性能的5個小貼士 翻譯:瘋狂的技術宅作者:Jennifer Oksnevad英文標題:5 Tips to ensure website performance英文原文:https://www.catswhocode.com/b....
摘要:定期進行負載測試負載測試顯示您的網站在一定數量的用戶訪問時的表現。如果負載測試顯示的頁面加載時間比預期的要長,那么網站設計的小改動就能帶來所需的改進。 確保網站性能的5個小貼士 翻譯:瘋狂的技術宅作者:Jennifer Oksnevad英文標題:5 Tips to ensure website performance英文原文:https://www.catswhocode.com/b....
摘要:在這個視頻中,將的調用棧回調隊列和事件循環的內容講的很清晰。調用棧可以往里面放東西,可以在事件結束的時候把回調函數放進回調隊列,然后是事件循環。為的時候這個過程看起來可能不明顯,除非考慮到調用棧的執行環境和事件循環的情況。 譯者按這篇文章可以看做是對Philip Roberts 2014年在JSConf演講的《What the heck is the event loop anyway...
摘要:當出現疾病或意外傷害時,可穿戴設備可以及時向醫院及周邊人群或家人發出求助。可穿戴設備聚集地國內關注可穿戴設備的用戶一定知道這個極客網站。隨著芯片體積的不斷減小,運算能力不斷地提高,未來,芯片將成為所有可穿戴設備的核心力量。 相信每個人都夢想擁有科幻電影中的智能腕表、神奇頭盔。隨著科技的日新月異,這個愿望看起來并不那么遙遠了。以智能手環、智能腕表、意念頭箍為代表的智能可穿戴設備已經越來越...
摘要:自選鎖鎖膨脹后,虛擬機為了避免線程真實地在操作系統層面掛起,虛擬機還會在做最后的努力自選鎖。 showImg(https://segmentfault.com/img/remote/1460000016159660?w=500&h=333); 作為一款公用平臺,JDK 本身也為并發程序的性能絞盡腦汁,在 JDK 內部也想盡一切辦法提供并發時的系統吞吐量。這里,我將向大家簡單介紹幾種 J...
閱讀 3834·2021-09-27 13:56
閱讀 881·2021-09-08 09:36
閱讀 765·2019-08-30 15:54
閱讀 609·2019-08-29 17:29
閱讀 927·2019-08-29 17:21
閱讀 1684·2019-08-29 16:59
閱讀 2757·2019-08-29 13:03
閱讀 2964·2019-08-29 12:47