激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - 編程技術 - 面試官問 async、await 函數原理是在問什么?

面試官問 async、await 函數原理是在問什么?

2022-01-05 23:11前端大全 編程技術

這周看的是 co 的源碼,我對 co 比較陌生,沒有了解和使用過。因此在看源碼之前,我希望能大概了解 co 是什么,解決了什么問題。

面試官問 async、await 函數原理是在問什么?

 1. 前言

這周看的是 co 的源碼,我對 co 比較陌生,沒有了解和使用過。因此在看源碼之前,我希望能大概了解 co 是什么,解決了什么問題。

2. 簡單了解 co

先看了 co 的 GitHub,README 是這樣介紹的:

Generator based control flow goodness for nodejs and the browser, using promises, letting you write non-blocking code in a nice-ish way.

看起來有點懵逼,又查了一些資料,大多說 co 是用于 generator 函數的自動執行。generator 是 ES6 提供的一種異步編程解決方案,它最大的特點是可以控制函數的執行。

2.1 關于 generator

說到異步編程,我們很容易想到還有 promise,async 和 await。它們有什么區別呢?先看看 JS 異步編程進化史:callback -> promise -> generator -> async + await

面試官問 async、await 函數原理是在問什么?

JS 異步編程

再看看它們語法上的差異:

Callback Promise Generator async + await + Promise
ajax(url, () => {}) Promise((resolve,reject) => { resolve() }).then() function* gen() { yield 1} async getData() {  await fetchData() }

關于 generator 的學習不在此篇幅詳寫了,需要了解它的概念和語法。

3. 學習目標

經過簡單學習,大概明白了 co 產生的背景,因為 generator 函數不會自動執行,需要手動調用它的 next() 函數,co 的作用就是自動執行 generator 的 next() 函數,直到 done 的狀態變成 true 為止。

那么我這一期的學習目標:

1)解讀 co 源碼,理解它是如何實現自動執行 generator

2)動手實現一個簡略版的 co

4. 解讀 co 源碼

co 源碼地址:https://github.com/tj/co

4.1 整體架構

從 README 中,可以看到是如何使用 co :

  1. co(function* () {  
  2.   var result = yield Promise.resolve(true);  
  3.   return result;  
  4. }).then(function (value) {  
  5.   console.log(value);  
  6. }, function (err) {  
  7.   console.error(err.stack);  
  8. }); 

從代碼可以看到它接收了一個 generator 函數,返回了一個 Promise,這部分對應的源碼如下。

  1. function co(gen) {  
  2.   var ctx = this 
  3.   // 獲取參數  
  4.   var args = slice.call(arguments, 1);  
  5.   // 返回一個 Promise  
  6.   return new Promise(function(resolve, reject) {  
  7.     // 把 ctx 和參數傳遞給 gen 函數  
  8.     if (typeof gen === 'function') gengen = gen.apply(ctx, args);  
  9.     // 判斷 gen.next 是否函數,如果不是直接 resolve(gen)  
  10.     if (!gen || typeof gen.next !== 'function') return resolve(gen);  
  11.     // 先執行一次 next  
  12.     onFulfilled();  
  13.     // 實際上就是執行 gen.next 函數,獲取 gen 的值  
  14.     function onFulfilled(res) {  
  15.       var ret;  
  16.       try {  
  17.         ret = gen.next(res);  
  18.       } catch (e) {  
  19.         return reject(e);  
  20.       }  
  21.       next(ret);  
  22.       return null;  
  23.     }  
  24.     // 對 gen.throw 的處理  
  25.     function onRejected(err) {  
  26.       var ret;  
  27.       try {  
  28.         ret = gen.throw(err);  
  29.       } catch (e) {  
  30.         return reject(e);  
  31.       }  
  32.       next(ret);  
  33.     }  
  34.     // 實際處理的函數,會遞歸執行,直到 ret.done 狀態為 true  
  35.     function next(ret) {  
  36.       // 如果生成器的狀態 done 為 true,就 resolve(ret.value),返回結果  
  37.       if (ret.done) return resolve(ret.value);  
  38.       // 否則,將 gen 的結果 value 封裝成 Promise  
  39.       var value = toPromise.call(ctx, ret.value);  
  40.       // 判斷 value 是否 Promise,如果是就返回 then 
  41.        if (value && isPromise(value)) return value.then(onFulfilled, onRejected);  
  42.       // 如果不是 Promise,Rejected  
  43.       return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '  
  44.         + 'but the following object was passed: "' + String(ret.value) + '"'));  
  45.     }  
  46.   }); 

看到這里,我產生了一個疑問:Promise + then 也可以處理異步編程,為什么 co 的源碼里要把 Promise + generator 結合起來呢,為什么要這樣做?直到我搞懂了 co 的核心目的,它使 generator 和 yield 的語法更趨向于同步編程的寫法,引用阮一峰的網絡日志中的一句話就是:

異步編程的語法目標,就是怎樣讓它更像同步編程。

可以看一個 Promise + then 的例子:

  1. function getData() {  
  2.   return new Promise(function(resolve, reject) {  
  3.     resolve(1111)  
  4.   })  
  5.  
  6. getData().then(function(res) {  
  7.   // 處理第一個異步的結果  
  8.   console.log(res);  
  9.   // 返回第二個異步  
  10.   return Promise.resolve(2222)  
  11. })  
  12. .then(function(res) {  
  13.   // 處理第二個異步的結果  
  14.   console.log(res)  
  15. })  
  16. .catch(function(err) {  
  17.   console.error(err);  
  18. }) 

如果有多個異步處理就會需要寫多少個 then 來處理異步之間可能存在的同步關系,從以上的代碼可以看到 then 的處理是一層一層的嵌套。如果換成 co,在寫法上更優雅也更符合日常同步編程的寫法:

  1. co(function* () {  
  2.   try {  
  3.     var result1 = yield Promise.resolve(1111)  
  4.     // 處理第一個異步的結果  
  5.     console.log(result1);  
  6.     // 返回第二個異步  
  7.     var result2 = yield Promise.resolve(2222)  
  8.     // 處理第二個異步的結果  
  9.     console.log(result2)  
  10.   } catch (err) {  
  11.     console.error(err)  
  12.   }  
  13. }); 

4.2 分析 next 函數

源碼的 next 函數接收一個 gen.next() 返回的對象 ret 作為參數,形如{value: T, done: boolean},next 函數只有四行代碼。

第一行:if (ret.done) return resolve(ret.value); 如果 ret.done 為 true,表明 gen 函數到了結束狀態,就 resolve(ret.value),返回結果。

第二行:var value = toPromise.call(ctx, ret.value); 調用 toPromise.call(ctx, ret.value) 函數,toPromise 函數的作用是把 ret.value 轉化成 Promise 類型,也就是用 Promise 包裹一層再 return 出去。

  1. function toPromise(obj) {  
  2.   // 如果 obj 不存在,直接返回 obj  
  3.   if (!obj) return obj;  
  4.   // 如果 obj 是 Promise 類型,直接返回 obj  
  5.   if (isPromise(obj)) return obj;  
  6.   // 如果 obj 是生成器函數或遍歷器對象, 就遞歸調用 co 函數 
  7.   if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);  
  8.   // 如果 obj 是普通的函數類型,轉換成 Promise 類型函數再返回  
  9.   if ('function' == typeof obj) return thunkToPromise.call(this, obj);  
  10.   // 如果 obj 是一個數組, 轉換成 Promise 數組再返回  
  11.   if (Array.isArray(obj)) return arrayToPromise.call(this, obj);  
  12.   // 如果 obj 是一個對象, 轉換成 Promise 對象再返回 
  13.   if (isObject(obj)) return objectToPromise.call(this, obj);  
  14.   // 其他情況直接返回  
  15.   return obj;  

第三行:if (value && isPromise(value)) return value.then(onFulfilled, onRejected); 如果 value 是 Promise 類型,調用 onFulfilled 或 onRejected,實際上是遞歸調用了 next 函數本身,直到 done 狀態為 true 或 throw error。

第四行:return onRejected(...) 如果不是 Promise,直接 Rejected。

5. 實踐

雖然解讀了 co 的核心代碼,看起來像是懂了,實際上很容易遺忘。為了加深理解,結合上面的 co 源碼和自己的思路動手實現一個簡略版的 co。

5.1 模擬請求 

  1. function request() {  
  2.   return new Promise((resolve) => {  
  3.     setTimeout(() => {  
  4.       resolve({data: 'request'});  
  5.     }, 1000);  
  6.   });  
  7.  
  8. // 用 yield 獲取 request 的值  
  9. function* getData() {  
  10.   yield request()  
  11.  
  12. var g = getData()  
  13. var {value, done} = g.next()  
  14. // 間隔1s后打印 {data: "request"}  
  15. value.then(res => console.log(res)) 

5.2 模擬實現簡版 co

核心實現:

1)函數傳參

2)generator.next 自動執行

  1. function co(gen) {  
  2.   // 1. 傳參  
  3.   var ctx = this 
  4.   const args = Array.prototype.slice.call(arguments, 1);  
  5.   gengen = gen.apply(ctx, args);  
  6.   return new Promise(function(resolve, reject) {  
  7.     // 2. 自動執行 next  
  8.     onFulfilled()  
  9.     function onFulfilled (res) {  
  10.       var ret = gen.next(res);  
  11.       next(ret);  
  12.     }   
  13.     function next(ret){  
  14.       if (ret.done) return resolve(ret.value);  
  15.       // 此處只處理 ret.value 是 Promise 對象的情況,其他類型簡略版沒處理  
  16.       var promise = ret.value;  
  17.       // 自動執行  
  18.       promise && promise.then(onFulfilled);  
  19.     }  
  20.   })  
  21.  
  22. // 執行  
  23. co(function* getData() {  
  24.   var result = yield request();  
  25.   // 1s后打印 {data: "request"}  
  26.   console.log(result)  
  27. }) 

6. 感想

學習一個新的東西(generator)花費的時間遠遠大于單純閱讀源碼的時間,因為需要了解它產生的背景,語法,解決的問題以及一些應用場景,這樣在閱讀源碼的時候才知道它為什么要這樣寫。

讀完源碼,我們會發現,其實 co 就是一個自動執行 next() 的函數,而且到最后我們會發現 co 的寫法和我們日常使用的 async/await 的寫法非常相像,因此也不難理解【async/await 實際上是對 generator 封裝的一個語法糖】這句話了。

  1. // co 寫法  
  2. co(function* getData() {  
  3.   var result = yield request();  
  4.   // 1s后打印 {data: "request"}  
  5.   console.log(result)  
  6. })  
  7. // async await 寫法  
  8. (async function getData() {  
  9.   var result = await request();  
  10.   // 1s后打印 {data: "request"}  
  11.   console.log(result)  
  12. })() 

不得不說,閱讀源碼的確是一個開闊視野的好方法。

原文地址:https://mp.weixin.qq.com/s/XhP_A3PVxhHHxMmbyl2Diw

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 午夜视频久久久 | 国产亚洲精品美女久久久 | 成人免费看片视频 | 国产91九色视频 | 天使萌一区二区三区免费观看 | 国产精品成人亚洲一区二区 | 一边吃奶一边摸下娇喘 | 亚洲人片在线观看 | 国产亚洲精品久久久久久久久 | 成人黄色免费小视频 | 亚洲码无人客一区二区三区 | 日本羞羞的午夜电视剧 | 精品国产乱码久久久久久丨区2区 | 一级片在线免费观看 | www.99久久久 | 久久精品免费网站 | 黄色网址进入 | 人人玩人人爽 | 91福利免费观看 | 蜜桃网在线 | 欧美成人三级大全 | 99久久久久国产精品免费 | 一本一本久久a久久精品综合小说 | 激情小说另类 | 欧美精品38videos性欧美 | 色婷婷久久久亚洲一区二区三区 | 欧美一级片免费在线观看 | 亚洲精品午夜国产va久久成人 | 中文字幕综合 | free国产hd老熟bbw | 精品国产一区二区亚洲人成毛片 | 老师你怎么会在这第2季出现 | 免费毛片在线视频 | a视频在线播放 | 国产亚洲欧美日韩高清 | 午夜视频福利 | 欧美顶级毛片在线播放小说 | 色999国产 | 亚欧美一区二区 | 成人免费一区二区三区 | 成年人黄视频 |