您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關es5中yield和es6的aysnc/await有什么用,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
最近的業余時間在看 js 相關的書, 也在極客時間上買了前端相關的專欄, 對于一個非 jser 的人來說, 時時會有一種感覺: js 社區是真的激進和浮燥, 這幫規則的制定者似乎從來也不知道克制為何物. 有很多時候固有的東西是可以處理好的, 但是偏偏喜歡人為制造一些概念和語法糖, 人為的建起一座又一座的高山, 似乎你沒跨過就是個 "菜雞"
請原諒我的惡毒, 看《js 語言精粹》的時候, 這種感覺非常的強烈. 作者是業內的大牛, 還制定了 json, 但是每一章還在最開頭引一句莎翁的話, "似乎無關緊要又暗藏哲理". 書中很多內容要表達的意思總有一種: 明明可以將話說成 10 分讓一個普通人都能看得明白的, 卻偏不, 只說 6 分, 剩下的自己去悟, 有很多規則性的東西并非是靠悟, 而是靠幾句話說清楚其本質就豁然開朗的.
換成以前, 會覺得這人是一座好大的高山要進行膜拜, 這幾年雖然自己技術依然不那么好, 但是還是喜歡去思考一些內在的東西, 更在努力一點一點把心里的權威崇拜給去掉, 再看到這些的時候, "..." 這幾個標點符號很容易印在腦子里. 感覺這不只是一兩個人這樣, 極有可能是整個 js 圈子都是這樣
說回標題上來, 除了看書, 看專欄, 找資料, 很久都還是沒有把 generator 和 async/await 給理解透, 于是自己試著整個梳理了一下運行的流程
我先試著在 yield 后面不跟任何的東西, 可以直接復制到控制臺輸出
function *f0(param) { console.log('n: ' + param); yield; console.log('i'); let l = yield; console.log('l: ' + l); } let v0 = f0('p'); console.log(v0.next(1)); // 輸出 n: p 和 {value: undefined, done: false} console.log('----'); console.log(v0.next(2)); // 輸出 i 和 {value: undefined, done: false} console.log('----'); console.log(v0.next(3)); // 輸出 l: 3 和 {value: undefined, done: true} console.log('----'); console.log(v0.next(4)); // 輸出 {value: undefined, done: true} console.log('----'); console.log(v0.next(5)); // 輸出 {value: undefined, done: true}
在上面的基礎上給方法 return 值
function *f1() { console.log('n'); yield; console.log('i'); let l = yield; console.log('l: ' + l); return '?'; } let v1 = f1(); console.log(v1.next(1)); // 輸出 n 和 {value: undefined, done: false} console.log('----'); console.log(v1.next(11)); // 輸出 i 和 {value: undefined, done: false} console.log('----'); console.log(v1.next(111)); // 輸出 l: 111 和 {value: '?', done: true} console.log('----'); console.log(v1.next(1111)); // 輸出 {value: undefined, done: true} console.log('----'); console.log(v1.next(11111)); // 輸出 {value: undefined, done: true}
然后我試著在 yield 的后面加上內容
function *f2(param) { console.log('0: ' + param); let f = yield 1; console.log('1: ' + f); let s = yield f + 2; console.log('2: ' + s); let t = yield (s + 3); console.log('3: ' + t); let fo = (yield s) + 4; console.log('4: ' + fo); } let v2 = f2('p'); console.log(v2.next('N')); // 輸出 0: p 和 {value: 1, done: false} console.log('----'); console.log(v2.next('I')); // 輸出 1: I 和 {value: "I2", done: false} console.log('----'); console.log(v2.next('L')); // 輸出 2: L 和 {value: "L3", done: false} console.log('----'); console.log(v2.next('S')); // 輸出 3: S 和 {value: "L", done: false} console.log('----'); console.log(v2.next('H')); // 輸出 4: H4 和 {value: undefined, done: true} console.log('----'); console.log(v2.next('I')); // 輸出 {value: undefined, done: true} console.log('----'); console.log(v2.next('T')); // 輸出 {value: undefined, done: true}
最后, 在上面的基礎上給方法 return 值
function *f3() { console.log('0'); let y1 = yield 1; console.log('1: ' + y1); let y2 = yield y1 + 2; console.log('2: ' + y2); let y3 = yield (y2 + 3); console.log('3: ' + y3); let y4 = (yield y3) + 4; console.log('4: ' + y4); return '??'; } let v3 = f3(); console.log(v3.next('N')); // 輸出 0 和 {value: 1, done: false} console.log('----'); console.log(v3.next('I')); // 輸出 1: I 和 {value: "I2", done: false} console.log('----'); console.log(v3.next('L')); // 輸出 2: L 和 {value: "L3", done: false} console.log('----'); console.log(v3.next('S')); // 輸出 3: S 和 {value: "S", done: false} console.log('----'); console.log(v3.next('H')); // 輸出 4: H4 和 {value: "??", done: true} console.log('----'); console.log(v3.next('I')); // 輸出 {value: undefined, done: true} console.log('----'); console.log(v3.next('T')); // 輸出 {value: undefined, done: true}
大致上就清楚 yield 的運行邏輯了, 以上面的 f3 為例, 對照上面的輸出來看, 它其實是將一個方法分成了這樣幾段來執行
// 下面 五行一起的豎線(|) 用一個大括號表示出來會更直觀一點 function *f3() { // 調用第 1 次 next('N') 時運行的代碼 console.log('0'); let y1 = yield 1; return 1; // | 封裝成 {value: 1, done: false} 返回 // | // | 這兩行等同于 let y1 = yield 1; // 調用第 2 次 next('I') 時運行的代碼 // | let y1 = 'I'; // | console.log('1: ' + y1); return y1 + 2; // | 封裝成 {value: "I2", done: false} 返回 // | // | 這兩行等同于 let y2 = yield y1 + 2; // 調用第 3 次 next('L') 時運行的代碼 // | let y2 = 'L'; // | console.log('2: ' + y2); return y2 + 3; // | 封裝成 {value: "L3", done: false} 返回 // | // | 這兩行等同于 let y3 = yield (y2 + 3); // 調用第 4 次 next('S') 時運行的代碼 // | let y3 = 'S'; // | console.log('3: ' + y3); return y3; // | 封裝成 {value: "S", done: false} 返回 // | // | 這兩行等同于 let y4 = (yield y3) + 4; // 調用第 5 次 next('H') 時運行的代碼 // | let y4 = 'H' // | console.log('4: ' + y4); return '??'; // 封裝成 {value: "??", done: true} 返回 }
再回頭想一想就知道了, 第一次運行 next('N') 的時候, 傳進去的 N 是會被忽略的, 因為第一次 next() 傳的值沒有 yield 前面來接收. 再去看書也好, 看查到的文章也好, 第一次 next() 都是沒有傳過參數
感覺 yield 就是為了迭代而生的, 迭代完全可以就用 for 啊, 但是卻繞成這樣, 也不知道這是為哪般! 就這還能新弄一個 for of 玩出花來, 因為每執行 next() 才會執行那一段段, 還美其名曰 "咱們終于可以異步了"
再是 es7 開始有的這倆關鍵字, 看了一個大廠的面試題, 自己為了加深對這兩個關鍵字的理解改了一下成下面這樣
async function async1() { console.log('A'); console.log(await async2()); return 'B'; } async function async2() { console.log('C'); return 'D'; } console.log('E'); setTimeout(function() { console.log('F'); }, 0); async1().then(function(r) { console.log(r); }); new Promise(function(resolve, reject) { console.log('G'); resolve(); }).then(function() { console.log('H'); }); console.log('I');
在 chrome 73.0.3683.75 底下的輸出是:
// 這個 undefined 的意思應該主要是用來分隔宏任務的, 也就是前面的主線和任務隊列是在一起的 E A C G I D H B undefined F
在 firefox 60.5.1 底下的輸出
// 這個 undefined 的意思應該只是用來分隔主線的, 任務隊列和宏任務在一起了 E A C G I undefined H D B F
在 opera 58.0.3135.107 底下的輸出是:
// 這個 undefined 應該跟 chrome 里面是一樣的 E A C G I H D B undefined F
明顯 D H B 是比較合理的. 在 firefox 和 opera 的實現中明顯是有問題的, 想像得到, 低版本一點的 chrome 也可能是后面的結果
還有像 var let const 這種一個簡單的賦值都能玩出這么多花樣(當然, 這可以說是歷史遺留問題導致的)
老實說, 我覺得這更多是為了: "別的語言有, 咱們這么前衛的語言當然應該要有!"
...
就這么樣一門語言, 居然可以流行成現在這樣, 只能說這個世界是真奇妙
關于es5中yield和es6的aysnc/await有什么用就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。