日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
深入探析koa之異步回調(diào)處理篇

在上一篇中我們梳理了koa當(dāng)中中間件的洋蔥模型執(zhí)行原理,并實(shí)現(xiàn)了一個(gè)可以讓洋蔥模型自動(dòng)跑起來(lái)的流程管理函數(shù)。這一篇,我們?cè)賮?lái)研究一下koa當(dāng)中異步回調(diào)同步化寫法的原理,同樣的,我們也會(huì)實(shí)現(xiàn)一個(gè)管理函數(shù),是的我們能夠通過(guò)同步化的寫法來(lái)寫異步回調(diào)函數(shù)。

專業(yè)領(lǐng)域包括網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、商城網(wǎng)站建設(shè)、微信營(yíng)銷、系統(tǒng)平臺(tái)開發(fā), 與其他網(wǎng)站設(shè)計(jì)及系統(tǒng)開發(fā)公司不同,創(chuàng)新互聯(lián)公司的整合解決方案結(jié)合了幫做網(wǎng)絡(luò)品牌建設(shè)經(jīng)驗(yàn)和互聯(lián)網(wǎng)整合營(yíng)銷的理念,并將策略和執(zhí)行緊密結(jié)合,為客戶提供全網(wǎng)互聯(lián)網(wǎng)整合方案。

1. 回調(diào)金字塔及理想中的解決方案

我們都知道javascript是一門單線程異步非阻塞語(yǔ)言。異步非阻塞當(dāng)然是它的一個(gè)優(yōu)點(diǎn),但大量的異步操作必然涉及大量的回調(diào)函數(shù),特別是當(dāng)異步嵌套的時(shí)候,就會(huì)出現(xiàn)回調(diào)金字塔的問(wèn)題,使得代碼的可讀性非常差。比如下面一個(gè)例子:

 
 
  1. var fs = require('fs'); 
  2.  
  3. fs.readFile('./file1', function(err, data) { 
  4.   console.log(data.toString()); 
  5.   fs.readFile('./file2', function(err, data) { 
  6.     console.log(data.toString()); 
  7.   }) 
  8. })  

這個(gè)例子是先后讀取兩個(gè)文件內(nèi)容并打印,其中file2的讀取必須在file1讀取結(jié)束之后再進(jìn)行,因此其操作必須要在file1讀取的回調(diào)函數(shù)中執(zhí)行。這是一個(gè)典型的回調(diào)嵌套,并且只有兩層而已,在實(shí)際編程中,我們可能會(huì)遇到更多層的嵌套,這樣的代碼寫法無(wú)疑是不夠優(yōu)雅的。

在我們想象中,比較優(yōu)雅的一種寫法應(yīng)該是看似同步實(shí)則異步的寫法,類似下面這樣:

 
 
  1. var data; 
  2. data = readFile('./file1'); 
  3. //下面的代碼是***個(gè)readFile執(zhí)行完畢之后的回調(diào)部分 
  4. console.log(data.toString()); 
  5. //下面的代碼是第二個(gè)readFile的回調(diào) 
  6. data = readFile('./file2'); 
  7. console.log(data.toString());  

這樣的寫法,就完全避免回調(diào)地獄。事實(shí)上,koa就讓我們可以使用這樣的寫法來(lái)寫異步回調(diào)函數(shù):

 
 
  1. var koa = require('koa'); 
  2. var app = koa(); 
  3. var request=require('some module'); 
  4.  
  5. app.use(function*() { 
  6.   var data = yield request('http://www.baidu.com'); 
  7.   //以下是異步回調(diào)部分 
  8.   this.body = data.toString(); 
  9. }) 
  10.  
  11. app.listen(3000); 

那么,究竟是什么讓koa有這么神奇的魔力呢?

2. generator配合promise實(shí)現(xiàn)異步回調(diào)同步寫法

關(guān)鍵的一點(diǎn),其實(shí)前一篇也提到了,就是generator具有類似"打斷點(diǎn)"這樣的效果。當(dāng)遇到y(tǒng)ield的時(shí)候,就會(huì)暫停,將控制權(quán)交給yield后面的函數(shù),當(dāng)下次返回的時(shí)候,再繼續(xù)執(zhí)行。

而在上面的那個(gè)koa例子中,yield后面的可不是任何對(duì)象都可以哦!必須是特定類型。在co函數(shù)中,可以支持promise, thunk函數(shù)等。

今天的文章中,我們就以promise為例來(lái)進(jìn)行分析,看看如何使用generator和promise配合,實(shí)現(xiàn)異步同步化。

依舊以***個(gè)讀取文件例子來(lái)分析。首先,我們需要將讀文件的函數(shù)進(jìn)行改造,將其封裝成為一個(gè)promise對(duì)象:

 
 
  1. var fs = require('fs'); 
  2.  
  3. var readFile = function(fileName) { 
  4.   return new Promise(function(resolve, reject) { 
  5.     fs.readFile(fileName, function(err, data) { 
  6.       if (err) { 
  7.         reject(err); 
  8.       } else { 
  9.         resolve(data); 
  10.       } 
  11.     }) 
  12.   }) 
  13.  
  14. //下面是readFile使用的示例 
  15. var tmp = readFile('./file1'); 
  16. tmp.then(function(data) { 
  17.   console.log(data.toString()); 
  18. })  

關(guān)于promise的使用,如果不熟悉的可以去看看es6中的語(yǔ)法。(近期我也會(huì)寫一篇文章來(lái)教大家如何用es5的語(yǔ)法來(lái)自己實(shí)現(xiàn)一個(gè)具備基本功能的promise對(duì)象,敬請(qǐng)期待呦^_^)

簡(jiǎn)單來(lái)講,promise可以實(shí)現(xiàn)將回調(diào)函數(shù)通過(guò) promise.then(callback)的形式來(lái)寫。但是我們的目標(biāo)是配合generator,真正實(shí)現(xiàn)如絲般順滑的同步化寫法,如何配合呢,看這段代碼:

 
 
  1. var fs = require('fs'); 
  2.  
  3. var readFile = function(fileName) { 
  4.   return new Promise(function(resolve, reject) { 
  5.     fs.readFile(fileName, function(err, data) { 
  6.       if (err) { 
  7.         reject(err); 
  8.       } else { 
  9.         resolve(data); 
  10.       } 
  11.     }) 
  12.   }) 
  13.  
  14. //將讀文件的過(guò)程放在generator中 
  15. var gen = function*() { 
  16.   var data = yield readFile('./file1'); 
  17.   console.log(data.toString()); 
  18.   data = yield readFile('./file2'); 
  19.   console.log(data.toString()); 
  20.  
  21. //手動(dòng)執(zhí)行g(shù)enerator 
  22. var g = gen(); 
  23. var another = g.next(); 
  24. //another.value就是返回的promise對(duì)象 
  25. another.value.then(function(data) { 
  26.   //再次調(diào)用g.next從斷點(diǎn)處執(zhí)行g(shù)enerator,并將data作為參數(shù)傳回 
  27.   var another2 = g.next(data); 
  28.   another2.value.then(function(data) { 
  29.     g.next(data); 
  30.   }) 
  31. })  

上述代碼中,我們?cè)趃enerator中yield了readFile,回調(diào)語(yǔ)句代碼寫在yield之后的代碼中,完全是同步的寫法,實(shí)現(xiàn)了文章一開頭的設(shè)想。

而yield之后,我們得到的是一個(gè)another.value是一個(gè)promise對(duì)象,我們可以使用then語(yǔ)句定義回調(diào)函數(shù),函數(shù)的內(nèi)容呢,則是將讀取到的data返回給generator并繼續(xù)讓generator從斷點(diǎn)處執(zhí)行。

基本上這就是異步回調(diào)同步化最核心的原理,事實(shí)上如果大家熟悉python,會(huì)知道python中有"協(xié)程"的概念,基本上也是使用generator來(lái)實(shí)現(xiàn)的(我想當(dāng)懷疑es6的generator就是借鑒了python~)

不過(guò)呢,上述代碼我們依然是手動(dòng)執(zhí)行的。那么同上一篇一樣,我們還需要實(shí)現(xiàn)一個(gè)run函數(shù),用于管理generator的流程,讓它能夠自動(dòng)跑起來(lái)!

3. 讓同步化回調(diào)函數(shù)自動(dòng)跑起來(lái):一個(gè)run函數(shù)的編寫

仔細(xì)觀察上一段代碼中手動(dòng)執(zhí)行g(shù)enerator的部分,也能發(fā)現(xiàn)一個(gè)規(guī)律,這個(gè)規(guī)律讓我們可以直接寫一個(gè)遞歸的函數(shù)來(lái)代替:

 
 
  1. var run=function(gen){ 
  2.   var g; 
  3.   if(typeof gen.next==='function'){ 
  4.     g=gen; 
  5.   }else{ 
  6.     g=gen(); 
  7.   } 
  8.  
  9.   function next(data){ 
  10.     var tmp=g.next(data); 
  11.     if(tmp.done){ 
  12.       return ; 
  13.     }else{ 
  14.       tmp.value.then(next); 
  15.     } 
  16.   } 
  17.  
  18.   next(); 
  19. }  

函數(shù)接收一個(gè)generator,并讓其中的異步能夠自動(dòng)執(zhí)行。使用這個(gè)run函數(shù),我們來(lái)讓上一個(gè)異步代碼自動(dòng)執(zhí)行:

 
 
  1. var fs = require('fs'); 
  2.  
  3. var run = function(gen) { 
  4.   var g; 
  5.   if (typeof gen.next === 'function') { 
  6.     g = gen; 
  7.   } else { 
  8.     g = gen(); 
  9.   } 
  10.  
  11.   function next(data) { 
  12.     var tmp = g.next(data); 
  13.     if (tmp.done) { 
  14.       return; 
  15.     } else { 
  16.       tmp.value.then(next); 
  17.     } 
  18.   } 
  19.  
  20.   next(); 
  21.  
  22. var readFile = function(fileName) { 
  23.   return new Promise(function(resolve, reject) { 
  24.     fs.readFile(fileName, function(err, data) { 
  25.       if (err) { 
  26.         reject(err); 
  27.       } else { 
  28.         resolve(data); 
  29.       } 
  30.     }) 
  31.   }) 
  32.  
  33. //將讀文件的過(guò)程放在generator中 
  34. var gen = function*() { 
  35.   var data = yield readFile('./file1'); 
  36.   console.log(data.toString()); 
  37.   data = yield readFile('./file2'); 
  38.   console.log(data.toString()); 
  39. //下面只需要將gen放入run當(dāng)中即可自動(dòng)執(zhí)行 
  40. run(gen);  

執(zhí)行上述代碼,即可看到終端依次打印出了file1和file2的內(nèi)容。

需要指出的是,這里的run函數(shù)為了簡(jiǎn)單起見只支持promise,而實(shí)際的co函數(shù)還支持thunk等。

這樣一來(lái),co函數(shù)的兩大功能基本就完整介紹了,一個(gè)是洋蔥模型的流程控制,另一個(gè)是異步同步化代碼的自動(dòng)執(zhí)行。在下一篇文章中,我將帶大家對(duì)這兩個(gè)功能進(jìn)行整合,寫出我們自己的一個(gè)co函數(shù)!

這篇文章的代碼同樣可以在github上面找到:https://github.com/mly-zju/async-js-demo,其中promise_generator.js就是本篇的示例源碼。


新聞名稱:深入探析koa之異步回調(diào)處理篇
網(wǎng)頁(yè)路徑:http://www.5511xx.com/article/cohiidi.html