自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

走近Node.js的異步代碼設(shè)計(jì)

譯文
開發(fā) 前端
許多企業(yè)目前在評(píng)估Node.js的異步、事件驅(qū)動(dòng)型的I/O,認(rèn)為這是一種高性能方案,可以替代多線程企業(yè)應(yīng)用服務(wù)器的傳統(tǒng)同步I/O。異步性質(zhì)意味著,企業(yè)開發(fā)人員必須學(xué)習(xí)新的編程模式,忘掉舊的編程模式。他們必須徹底轉(zhuǎn)變思路,可能需要借助電擊療法^_^。本文介紹了如何將舊的同步編程模式換成全新的異步編程模式。

【51CTO精選譯文】許多企業(yè)目前在評(píng)估Node.js的異步、事件驅(qū)動(dòng)型的I/O,認(rèn)為這是一種高性能方案,可以替代多線程企業(yè)應(yīng)用服務(wù)器的傳統(tǒng)同步I/O。異步性質(zhì)意味著,企業(yè)開發(fā)人員必須學(xué)習(xí)新的編程模式,忘掉舊的編程模式。他們必須徹底轉(zhuǎn)變思路,可能需要借助電擊療法^_^。本文介紹了如何將舊的同步編程模式換成全新的異步編程模式。

51CTO推薦專題:Node.js專區(qū)

開始轉(zhuǎn)變思路

要使用Node.js,就有必要了解異步編程的工作原理。異步代碼設(shè)計(jì)并非簡(jiǎn)單的設(shè)計(jì),需要一番學(xué)習(xí)?,F(xiàn)在需要來(lái)一番電擊療法:本文在同步代碼示例旁邊給出了異步代碼示例,表明如何更改同步代碼,才能變成異步代碼。這些示例都圍繞Node.js的文件系統(tǒng)(fs)模塊,因?yàn)樗俏ㄒ缓型絀/O操作及異步I/O操作的模塊。有了這兩種示例,你可以開始轉(zhuǎn)變思路了。

相關(guān)代碼和獨(dú)立代碼

回調(diào)函數(shù)(callback function)是Node.js中異步事件驅(qū)動(dòng)型編程的基本構(gòu)建模塊。它們是作為變量,傳遞給異步I/O操作的函數(shù)。一旦操作完成,回調(diào)函數(shù)就被調(diào)用?;卣{(diào)函數(shù)是Node.js中實(shí)現(xiàn)事件的機(jī)制。

下面顯示的示例表明了如何將同步I/O操作轉(zhuǎn)換成異步I/O操作,并顯示了回調(diào)函數(shù)的使用。示例使用異步fs.readdirSync()調(diào)用,讀取當(dāng)前目錄的文件名稱,然后把文件名稱記錄到控制臺(tái),***讀取當(dāng)前進(jìn)程的進(jìn)程編號(hào)(process id)。

同步

  1. var fs = require('fs'),  
  2.     filenames,  
  3.     i,  
  4.     processId;  
  5. filenames = fs.readdirSync(".");  
  6. for (i = 0; i < filenames.length; i++) {  
  7.     console.log(filenames[i]);  
  8. }  
  9. console.log("Ready.");  
  10. processprocessId = process.getuid();  

異步

  1. var fs = require('fs'),  
  2.     processId;  
  3. fs.readdir(".", function (err, filenames) {  
  4.     var i;  
  5.     for (i = 0; i < filenames.length; i++) {  
  6.         console.log(filenames[i]);  
  7.     }  
  8.     console.log("Ready.");  
  9. });  
  10. processprocessId = process.getuid(); 

在同步示例中,處理器等待fs.readdirSync() I/O操作,所以這是需要更改的操作。Node.js中該函數(shù)的異步版本是fs.readdir()。它與fs.readdirSync()一樣,但是回調(diào)函數(shù)作為第二個(gè)參數(shù)。

使用回調(diào)函數(shù)模式的規(guī)則如下:把同步函數(shù)換成對(duì)應(yīng)的異步函數(shù),然后把原先在同步調(diào)用后執(zhí)行的代碼放在回調(diào)函數(shù)里面。回調(diào)函數(shù)中的代碼與同步示例中的代碼執(zhí)行一模一樣的操作。它把文件名稱記錄到控制臺(tái)。它在異步I/O操作返回之后執(zhí)行。

就像文件名稱的記錄依賴fs.readdirSync() I/O操作的結(jié)果,所列文件數(shù)量的記錄也依賴其結(jié)果。進(jìn)程編號(hào)的存儲(chǔ)獨(dú)立于I/O操作的結(jié)果。因而,必須把它們移到異步代碼中的不同位置。

規(guī)則就是將相關(guān)代碼移到回調(diào)函數(shù)中,而獨(dú)立代碼的位置不用管。一旦I/O操作完成,相關(guān)代碼就被執(zhí)行,而獨(dú)立代碼在I/O操作被調(diào)用之后立即執(zhí)行。

順序

同步代碼中的標(biāo)準(zhǔn)模式是線性順序:幾行代碼都必須下一行接上一行來(lái)執(zhí)行,因?yàn)槊恳恍写a依賴上一行代碼的結(jié)果。在下面示例中,代碼首先變更了文件的訪問(wèn)模式(比如Unix chmod命令),對(duì)文件更名,然后檢查更名后文件是不是符號(hào)鏈接。很顯然,該代碼無(wú)法亂序運(yùn)行,不然文件在模式變更前就被更名了,或者符號(hào)鏈接檢查在文件被更名前就執(zhí)行了。這兩種情況都會(huì)導(dǎo)致出錯(cuò)。因而,順序必須予以保留。

同步

  1. var fs = require('fs'),  
  2.     oldFilename,  
  3.     newFilename,  
  4.     isSymLink;  
  5. oldFilename = "./processId.txt";  
  6. newFilename = "./processIdOld.txt";  
  7. fs.chmodSync(oldFilename, 777);  
  8. fs.renameSync(oldFilename, newFilename);  
  9. isSymLink = fs.lstatSync(newFilename).isSymbolicLink(); 

異步

  1. var fs = require('fs'),  
  2.     oldFilename,  
  3.     newFilename;  
  4. oldFilename = "./processId.txt";  
  5. newFilename = "./processIdOld.txt";  
  6. fs.chmod(oldFilename, 777, function (err) {     
  7.     fs.rename(oldFilename, newFilename, function (err) {  
  8.         fs.lstat(newFilename, function (err, stats) {  
  9.             var isSymLink = stats.isSymbolicLink();  
  10.         });  
  11.     });  
  12. });  

在異步代碼中,這些順序變成了嵌套回調(diào)。該示例顯示了fs.lstat()回調(diào)嵌套在fs.rename()回調(diào)里面,而fs.rename()回調(diào)嵌套在fs.chmod()回調(diào)里面。

#p#

并行處理

異步代碼特別適合操作I/O操作的并行處理:代碼的執(zhí)行并不因I/O調(diào)用的返回而受阻。多個(gè)I/O操作可以并行開始。在下面示例中,某個(gè)目錄中所有文件的大小都在循環(huán)中累加,以獲得那些文件占用的總字節(jié)數(shù)。使用異步代碼,循環(huán)的每次迭代都必須等到獲取單個(gè)文件大小的I/O調(diào)用返回為止。

異步代碼允許快速連續(xù)地在循環(huán)中開始所有I/O調(diào)用,不用等結(jié)果返回。只要其中一個(gè)I/O操作完成,回調(diào)函數(shù)就被調(diào)用,而該文件的大小就可以添加到總字節(jié)數(shù)中。

唯一必不可少的有一個(gè)恰當(dāng)?shù)耐V箻?biāo)準(zhǔn),它決定著我們完成處理后,就計(jì)算所有文件的總字節(jié)數(shù)。

同步

  1. var fs = require('fs');  
  2. function calculateByteSize() {  
  3.     var totalBytes = 0,  
  4.         i,  
  5.         filenames,  
  6.         stats;  
  7.     filenames = fs.readdirSync(".");  
  8.     for (i = 0; i < filenames.length; i ++) {  
  9.         stats = fs.statSync("./" + filenames[i]);  
  10.         totalBytes += stats.size;  
  11.     }  
  12.     console.log(totalBytes);  
  13. }  
  14.  
  15.  
  16.  
  17. calculateByteSize(); 

異步

  1. var fs = require('fs');  
  2. var count = 0,  
  3.     totalBytes = 0;  
  4. function calculateByteSize() {  
  5.     fs.readdir(".", function (err, filenames) {  
  6.         var i;  
  7.         count = filenames.length;  
  8.         for (i = 0; i < filenames.length; i++) {  
  9.             fs.stat("./" + filenames[i], function (err, stats) {  
  10.                 totalBytes += stats.size;  
  11.                 count--;  
  12.                 if (count === 0) {  
  13.                     console.log(totalBytes);  
  14.                 }  
  15.             });  
  16.         }  
  17.     });  
  18. }  
  19. calculateByteSize(); 

同步示例簡(jiǎn)單又直觀。在異步版本中,***個(gè)fs.readdir()被調(diào)用,以讀取目錄中的文件名稱。在回調(diào)函數(shù)中,針對(duì)每個(gè)文件調(diào)用fs.stat(),返回該文件的統(tǒng)計(jì)信息。這部分不出所料。

值得關(guān)注的方面出現(xiàn)在計(jì)算總字節(jié)數(shù)的fs.stat()回調(diào)函數(shù)中。所用的停止標(biāo)準(zhǔn)是目錄的文件數(shù)量。變量count以文件數(shù)量來(lái)初始化,倒計(jì)數(shù)回調(diào)函數(shù)執(zhí)行的次數(shù)。一旦數(shù)量為0,所有I/O操作都被回調(diào),所有文件的總字節(jié)數(shù)被計(jì)算出來(lái)。計(jì)算完畢后,字節(jié)數(shù)可以記錄到控制臺(tái)。

異步示例有另一個(gè)值得關(guān)注的特性:它使用閉包(closure)。閉包是函數(shù)里面的函數(shù),內(nèi)層函數(shù)訪問(wèn)外層函數(shù)中聲明的變量,即便在外層函數(shù)已完成之后。fs.stat()回調(diào)函數(shù)是閉包,因?yàn)樗缭趂s.readdir()回調(diào)函數(shù)完成后,訪問(wèn)在該函數(shù)中聲明的count和totalBytes這兩個(gè)變量。閉包有關(guān)于它自己的上下文。在該上下文中,可以放置在函數(shù)中訪問(wèn)的變量。

要是沒(méi)有閉包,count和totalBytes這兩個(gè)變量都必須是全局變量。這是由于fs.stat()回調(diào)函數(shù)沒(méi)有放置變量的任何上下文。calculateBiteSize()函數(shù)早已結(jié)束,只有全局上下文仍在那里。這時(shí)候閉包就能派得上用場(chǎng)。變量可以放在該上下文中,那樣可以從函數(shù)里面訪問(wèn)它們。

代碼復(fù)用

代碼片段可以在JavaScript中復(fù)用,只要把代碼片段包在函數(shù)里面。然后,可以從程序中的不同位置調(diào)用這些函數(shù)。如果函數(shù)中使用了I/O操作,那么改成異步代碼時(shí),就需要某種重構(gòu)。

下面的異步示例顯示了返回某個(gè)目錄中文件數(shù)量的函數(shù)countFiles()。countFiles()使用I/O操作fs.readdirSync() 來(lái)確定文件數(shù)量。span style="font-family: courier new,courier;">countFiles()本身被調(diào)用,使用兩個(gè)不同的輸入?yún)?shù):

同步

  1. var fs = require('fs');  
  2. var path1 = "./",  
  3.     path2 = ".././";  
  4. function countFiles(path) {  
  5.     var filenames = fs.readdirSync(path);  
  6.     return filenames.length;  
  7. }  
  8. console.log(countFiles(path1) + " files in " + path1);  
  9. console.log(countFiles(path2) + " files in " + path2); 

異步

  1. var fs = require('fs');  
  2. var path1 = "./",  
  3.     path2 = ".././",  
  4.     logCount;  
  5. function countFiles(path, callback) {  
  6.     fs.readdir(path, function (err, filenames) {  
  7.         callback(err, path, filenames.length);  
  8.     });  
  9. }  
  10. logCount = function (err, path, count) {  
  11.     console.log(count + " files in " + path);  
  12. };  
  13. countFiles(path1, logCount);   
  14. countFiles(path2, logCount);  

把fs.readdirSync()換成異步fs.readdir()迫使閉包函數(shù)cntFiles()也變成異步,因?yàn)檎{(diào)用cntFiles()的代碼依賴該函數(shù)的結(jié)果。畢竟,只有fs.readdir()返回后,結(jié)果才會(huì)出現(xiàn)。這導(dǎo)致了cntFiles()重構(gòu),以便還能接受回調(diào)函數(shù)。整個(gè)控制流程突然倒過(guò)來(lái)了:不是console.log()調(diào)用cntFiles(),cntFiles()再調(diào)用fs.readdirSync(),在異步示例中,而是cntFiles()調(diào)用fs.readdir(),然后cntFiles()再調(diào)用console.log()。

結(jié)束語(yǔ)

本文著重介紹了異步編程的一些基本模式。將思路轉(zhuǎn)變到異步編程絕非易事,需要一段時(shí)間來(lái)適應(yīng)。雖然難度增加了,但是獲得的回報(bào)是顯著提高了并發(fā)性。結(jié)合JavaScript的快速周轉(zhuǎn)和易于使用等優(yōu)點(diǎn),Node.js中的異步編程有望在企業(yè)應(yīng)用市場(chǎng)取得進(jìn)展,尤其是在新一代高度并發(fā)性的Web 2.0應(yīng)用程序方面。

原文:http://shinetech.com/thoughts/thought-articles/139-asynchronous-code-design-with-nodejs
 

【編輯推薦】

  1. 使用Node.js開發(fā)多人玩的HTML 5游戲
  2. Node.js提速指南
  3. Node.js專區(qū)
  4. 什么是Node.js?
  5. 使用node.js進(jìn)行服務(wù)器端JavaScript編程
責(zé)任編輯:陳貽新 來(lái)源: 51CTO
相關(guān)推薦

2021-04-06 10:15:29

Node.jsHooks前端

2025-01-13 00:00:00

2021-03-04 23:12:57

Node.js異步迭代器開發(fā)

2021-03-16 16:16:41

GeneratorWebsockets前端

2012-02-02 15:14:29

Node.js

2021-01-26 08:07:44

Node.js模塊 Async

2020-12-08 06:28:47

Node.js異步迭代器

2013-11-01 09:34:56

Node.js技術(shù)

2022-04-02 06:04:03

Node.js代碼緩存V8

2015-03-10 10:59:18

Node.js開發(fā)指南基礎(chǔ)介紹

2012-01-10 10:04:43

Node.js

2020-05-29 15:33:28

Node.js框架JavaScript

2021-10-22 08:29:14

JavaScript事件循環(huán)

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

2012-02-03 09:25:39

Node.js

2021-09-07 07:53:43

工具

2011-11-01 10:30:36

Node.js

2011-09-02 14:47:48

Node

2011-09-09 14:23:13

Node.js

2011-09-08 13:46:14

node.js
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)