├── CCBY.png ├── LICENSE.md ├── code ├── 1.js ├── 2.js ├── 3.js ├── 4.js └── number.txt ├── donate.png ├── mod-diagram-01.png ├── mod-diagram-02.png ├── npm-search.png ├── pdf-code.png ├── pdf-modules.png ├── pdf-readmes.png ├── readme.es.md ├── readme.fr.md ├── readme.md ├── readme.pt-br.md ├── readme.ru.md ├── readme.zh-cn.md ├── readme.zh-tw.md ├── requirebin.png ├── server-diagram.png ├── simple-module.png ├── stream-adventure.png └── stream-handbook.png /CCBY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/CCBY.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Creative Commons Share Alike (do whatever, just attribute me) 2 | http://creativecommons.org/licenses/by-sa/2.0/ -------------------------------------------------------------------------------- /code/1.js: -------------------------------------------------------------------------------- 1 | // cd code/ 2 | // node 1.js 3 | 4 | var myNumber = 1 5 | function addOne() { myNumber++ } // define the function 6 | addOne() // run the function 7 | console.log(myNumber) // logs out 2 -------------------------------------------------------------------------------- /code/2.js: -------------------------------------------------------------------------------- 1 | // cd code/ 2 | // node 2.js 3 | 4 | var fs = require('fs') 5 | var myNumber = undefined // we dont know what the number is yet since it is stored in a file 6 | 7 | function addOne() { 8 | fs.readFile('./number.txt', function doneReading(err, fileContents) { 9 | myNumber = parseInt(fileContents) 10 | myNumber++ 11 | }) 12 | } 13 | 14 | addOne() 15 | 16 | console.log(myNumber) // logs out undefined -------------------------------------------------------------------------------- /code/3.js: -------------------------------------------------------------------------------- 1 | // cd code/ 2 | // node 3.js 3 | 4 | var fs = require('fs') 5 | var myNumber = undefined 6 | 7 | function addOne(callback) { 8 | fs.readFile('number.txt', function doneReading(err, fileContents) { 9 | myNumber = parseInt(fileContents) 10 | myNumber++ 11 | callback() 12 | }) 13 | } 14 | 15 | function logMyNumber() { 16 | console.log(myNumber) 17 | } 18 | 19 | addOne(logMyNumber) -------------------------------------------------------------------------------- /code/4.js: -------------------------------------------------------------------------------- 1 | function takesOneSecond(callback) { 2 | setTimeout(after, 1000) 3 | function after() { 4 | console.log('done with one, on to the next') 5 | if (callback) callback() 6 | } 7 | } 8 | 9 | a = takesOneSecond, b = takesOneSecond, c = takesOneSecond 10 | 11 | a(function() { 12 | b(function() { 13 | c() 14 | }) 15 | }) -------------------------------------------------------------------------------- /code/number.txt: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/donate.png -------------------------------------------------------------------------------- /mod-diagram-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/mod-diagram-01.png -------------------------------------------------------------------------------- /mod-diagram-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/mod-diagram-02.png -------------------------------------------------------------------------------- /npm-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/npm-search.png -------------------------------------------------------------------------------- /pdf-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/pdf-code.png -------------------------------------------------------------------------------- /pdf-modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/pdf-modules.png -------------------------------------------------------------------------------- /pdf-readmes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/pdf-readmes.png -------------------------------------------------------------------------------- /readme.es.md: -------------------------------------------------------------------------------- 1 | # El Arte de Node 2 | ## Una introducción a Node.js 3 | 4 | Este documento está destinado a aquellos lectores que saben al menos un poco de esto: 5 | 6 | - un lenguaje tipo script como JavaScript, Ruby, Python, Perl, etc. Si aun no eres programador entonces probablemente será más fácil empezar por leer [JavaScript for Cats](http://jsforcats.com/). :cat2: 7 | - git y github. Éstas son herramientas colaborativas de código abierto usadas por las personas en la comunidad de Node para compartir módulos. Sólo necesitas conocer lo básico. Aquí hay tres tutoriales introductorios: [1](https://github.com/jlord/git-it-electron#readme), [2](http://zachbruggeman.me/github-for-cats/), [3](http://opensourcerer.diy.org/) 8 | 9 | Este corto libro es un trabajo en progeso + No tengo trabajo en este momento (si tuviera no tendría el tiempo para escribir esto). Si te gusta este trabajo considera realizar una donación via [gittip](https://www.gittip.com/maxogden/) para poder escribir mucho más! 10 | 11 | [![donate](donate.png)](https://www.gittip.com/maxogden/) 12 | 13 | ## Tabla de contenidos 14 | 15 | - [Entendiendo node](#entendiendo-node) 16 | - [Módulos en el core](#mdulos-en-el-core) 17 | - [Retrollamadas](#retrollamadas) 18 | - [Events](#events) (not written yet) 19 | - [Streams](#streams) (not written yet) 20 | - [Modules and NPM](#modules) (not written yet) 21 | - [Going with the grain](#going-with-the-grain) 22 | - [Real-time apps](#realtime) (not written yet) 23 | 24 | ## Entendiendo node 25 | 26 | Node.js es un proyecto de código abierto diseñado para ayudarte a escribir código JavaScript que se comunique con redes, archivos del sistema y otras E/S (entrada/salida, lectura/escritura) fuentes. Eso es! Node.js es simplemente una sencilla y estable plataforma E/S que anima a construir módulos sobre ella misma. 27 | 28 | ¿Cuáles son algunos de E/S? Aquí hay un diagrama de una aplicación que hice con Node y que muestra algunas fuentes de E/S: 29 | 30 | ![server diagram](server-diagram.png) 31 | 32 | Si no entiendes todos los elementos en el diagrama está bien. El punto es mostrar que un único proceso en Node (el hexágono en la mitad) puede actuar como agente entre todos los diferentes puntos finales de E/S (naranja y púrpura representan E/S). 33 | 34 | Al construir uno de estos tipos de sistemas usualmente se presenta uno de estos casos: 35 | 36 | - dificultad para programar pero con muy buenos y rápidos resultados (como escribir tus servidores web desde cero en C) 37 | - Fácil de codificar pero no muy rápido/robusto (como cuando intentas subir un archivo de 5GB y tu servidor colapsa) 38 | 39 | El objetivo de Node es proveer un balance entre: relativamente fácil de entender y usar, y lo suficientemente rápido para la mayoría de los casos. 40 | 41 | Node no es ninguno de los siguientes: 42 | 43 | - Un framework web (como Rails o Django, sin embargo puede ser usado para hacer tales cosas) 44 | - Un lenguaje de programación (Node usa JavaScript pero no es en sí mismo un lenguaje) 45 | 46 | En su lugar, Node es algo en la mitad: 47 | 48 | - Diseñado para ser simple y por lo tanto relativamente fácil de entender y usar 49 | - Útil para programas basados en E/S que necesitan ser rápidos y/o manejar muchas conecciones 50 | 51 | A un nivel más bajo, Node puede ser descrito como una herramienta para escribir dos grandes tipos de programas: 52 | 53 | - Programas de red que usen los protocolos de la web: HTTP, TCP, UDP, DNS and SSL 54 | - Programas que lean y escriban información al sistema de archivos o a los procesos/memoria local 55 | 56 | ¿Qué es un "programa basado en E/S"? Aquí hay alguna fuentes comunes de E/S: 57 | 58 | - Bases de datos (MySQL, PostgreSQL, MongoDB, Redis, CouchDB) 59 | - APIs (Twitter, Facebook, Apple Push Notifications) 60 | - Conecciones HTTP/WebSocket (desde usuarios de una aplicación web) 61 | - Archivos (editor de imágenes, editor de videos, radio por internet) 62 | 63 | Node procesa E/S en una forma llamada [asíncrona](http://en.wikipedia.org/wiki/Asynchronous_I/O) el cual le permite manejar muchas cosas diferentes simultáneamente. Por ejemplo, si vas a un restaurante de comida rápida y ordenas una hamburguesa ellos tomarán tu orden inmediatamente y te harán esperar hasta que tu hamburguesa esté lista. Mientras tanto ellos pueden tomar otras órdenes y empezar a preparar hamburguesas para otras personas. Imagina si tuvieras que esperar en la caja registradora por tu hamburguesa, bloqueando a todas las otras personas en la fila para ordenar, mientras preparan tu hamburguesa! Esto es llamado **bloqueo de E/S** porque toda E/S (preparación de hamburguesas) suceden una vez al tiempo. Node, por otro lado, es de **no-bloque**, que significa que puede preparar muchas hamburguesas al tiempo. 64 | 65 | Aquí hay algunas cosas divertidas hechas de manera fácil con Node gracias a su naturaleza de no-bloque: 66 | 67 | - Control [volando quadcopters](http://nodecopter.com) 68 | - Escribir bots para chat IRC 69 | - Crear [robots bípedos](http://www.youtube.com/watch?v=jf-cEB3U2UQ) 70 | 71 | ## Módulos en el core 72 | 73 | Primero que todo recomiendo tener instalado Node en tu computadora. La forma más fácil es visitar [nodejs.org](http://nodejs.org) y dar click en `Install`. 74 | 75 | Node tiene un pequeño grupo de módulos en el core (comunmente referenciados como 'core de node') los cuales son presentados como la API pública que tienen por objeto el escribir programas con ellos. Para trabajar con sistemas de archivos está el módulo `fs` y para redes existen módulos como `net` (TCP), `http`, `dgram` (UDP). 76 | 77 | Adicionalmente a los módulos `fs` y de redes, hay otros módulos base en el core de node. Existe un módulo para resolver consultas DNS asincronamente llamado `dns`, un módulo para obtener información específica del SO como el directorio temporal, llamado `os`, un módulo para asignar pedazos binarios de memoria llamado `buffer`, algunos módulos para parsear urls y caminos (`url`, `querystring`, `path`), etc. La mayoría si no todos los módulos en el core de Node, están para soportar los casos de uso principales de Node: Escribir programas rápidos que se comuniquen con sistemas de archivos o redes. 78 | 79 | Node maneja E/S con: callbacks, eventos, streams (flujos) y módulos. Si aprendes cómo trabajan esos cuatro elementos entonces serás capaz de ir dentro de cualquier módulo en el core de Node y entender básicamente sobre cómo interactuar con él. 80 | 81 | ## Retrollamadas 82 | 83 | Este es el tema más importante para entender si quieres entender cómo utilizar node. Casi todo en node utiliza retrollamadas. No fueron inventadas por node, y son una forma particularmente útil para utilizar las funciones en JavaScript. 84 | 85 | Las retrollamadas son funciones que se ejecutan de forma asíncrona, o en un momento posterior. En lugar de leer el código de arriba a abajo, programas asincrónicos pueden ejecutar diferentes funciones en diferentes momentos basado en el orden y la velocidad que ocurren las funciones que leen el sistema de archivo o los pedidos de http. 86 | 87 | Determinando si una función es asíncrona o no puede crear confusión ya que depende mucho en el contexto donde se presenta. Aquí sigue un ejemplo simple de una función sincrónica: 88 | 89 | 90 | ```js 91 | var miNumero = 1 92 | function agregaUno() { miNumeror++ } // define la función 93 | agregaUno() // ejecuta la función 94 | console.log(miNumero) // registra 2 95 | ``` 96 | 97 | ++++++++++++++++++++++++Continue Translation+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 98 | 99 | The code here defines a function and then on the next line calls that function, without waiting for anything. When the function is called it immediately adds 1 to the number, so we can expect that after we call the function the number should be 2. 100 | 101 | Let's suppose that we want to instead store our number in a file called `number.txt`: 102 | 103 | ```js 104 | var fs = require('fs') // require is a special function provided by node 105 | var myNumber = undefined // we dont know what the number is yet since it is stored in a file 106 | 107 | function addOne() { 108 | fs.readFile('./number.txt', function doneReading(err, fileContents) { 109 | myNumber = parseInt(fileContents) 110 | myNumber++ 111 | }) 112 | } 113 | 114 | addOne() 115 | 116 | console.log(myNumber) // logs out undefined 117 | ``` 118 | 119 | Why do we get `undefined` when we log out the number this time? In this code we use the `fs.readFile` method, which happens to be an asynchronous method. Usually things that have to talk to hard drives or networks will be asynchronous. If they just have to access things in memory or do some work on the CPU they will be synchronous. The reason for this is that I/O is reallyyy reallyyy sloowwww. A ballpark figure would be that talking to a hard drive is about 100,000 times slower than talking to memory (RAM). 120 | 121 | When we run this program all of the functions are immediately defined, but they don't all execute immediately. This is a fundamental thing to understand about async programming. When `addOne` is called it kicks off a `readFile` and then moves on to the next thing that is ready to execute. If there is nothing to execute node will either wait for pending fs/network operations to finish or it will stop running and exit to the command line. 122 | 123 | When `readFile` is done reading the file (this may take anywhere from milliseconds to seconds to minutes depending on how fast the hard drive is) it will run the `doneReading` function and give it an error (if there was an error) and the file contents. 124 | 125 | The reason we got `undefined` above is that nowhere in our code exists logic that tells the `console.log` statement to wait until the `readFile` statement finishes before it prints out the number. 126 | 127 | If you have some code that you want to be able to execute over and over again or at a later time the first step is to put that code inside a function. Then you can call the function whenever you want to run your code. It helps to give your functions descriptive names. 128 | 129 | Callbacks are just functions that get executed at some later time. The key to understanding callbacks is to realize that they are used when you don't know **when** some async operation will complete, but you do know **where** the operation will complete — the last line of the async function! The top-to-bottom order that you declare callbacks does not necessarily matter, only the logical/hierarchical nesting of them. First you split your code up into functions, and then use callbacks to declare if one function depends on another function finishing. 130 | 131 | The `fs.readFile` method is provided by node, is asynchronous and happens to take a long time to finish. Consider what it does: it has to go to the operating system, which in turn has to go to the file system, which lives on a hard drive that may or may not be spinning at thousands of revolutions per minute. Then it has to use a laser to read data and send it back up through the layers back into your javascript program. You give `readFile` a function (known as a callback) that it will call after it has retrieved the data from the file system. It puts the data it retrieved into a javascript variable and calls your function (callback) with that variable, in this case the variable is called `fileContents` because it contains the contents of the file that was read. 132 | 133 | Think of the restaurant example at the beginning of this tutorial. At many restaurants you get a number to put on your table while you wait for your food. These are a lot like callbacks. They tell the server what to do after your cheeseburger is done. 134 | 135 | Let's put our `console.log` statement into a function and pass it in as a callback. 136 | 137 | ```js 138 | var fs = require('fs') 139 | var myNumber = undefined 140 | 141 | function addOne(callback) { 142 | fs.readFile('./number.txt', function doneReading(err, fileContents) { 143 | myNumber = parseInt(fileContents) 144 | myNumber++ 145 | callback() 146 | } 147 | } 148 | 149 | function logMyNumber() { 150 | console.log(myNumber) 151 | } 152 | 153 | addOne(logMyNumber) 154 | ``` 155 | 156 | Now the `logMyNumber` function can get passed in an argument that will become the `callback` variable inside the `addOne` function. After `readFile` is done the `callback` variable will be invoked (`callback()`). Only functions can be invoked, so if you pass in anything other than a function it will cause an error. 157 | 158 | When a function get invoked in javascript the code inside that function will immediately get executed. In this case our log statement will execute since `callback` is actually `logMyNumber`. Remember, just because you *define* a function it doesn't mean it will execute. You have to *invoke* a function for that to happen. 159 | 160 | To break down this example even more, here is a timeline of events that happen when we run this program: 161 | 162 | - 1: the code is parsed, which means if there are any syntax errors they would make the program break. 163 | - 2: `addOne` gets invoked, getting passed in the `logMyNumber` function as `callback`, which is what we want to be called when `addOne` is complete. This immediately causes the asynchronous `fs.readFile` function to kick off. This part of the program takes a while to finish. 164 | - 3: with nothing to do, node idles for a bit as it waits for `readFile` to finish 165 | - 4: `readFile` finishes and calls its callback, `doneReading`, which then in turn increments the number and then immediately invokes the function that `addOne` passed in (its callback), `logMyNumber`. 166 | 167 | Perhaps the most confusing part of programming with callbacks is how functions are just objects that be stored in variables and passed around with different names. Giving simple and descriptive names to your variables is important in making your code readable by others. Generally speaking in node programs when you see a variable like `callback` or `cb` you can assume it is a function. 168 | 169 | You may have heard the terms 'evented programming' or 'event loop'. They refer to the way that `readFile` is implemented. Node first dispatches the `readFile` operation and then waits for `readFile` to send it an event that it has completed. While it is waiting node can go check on other things. Inside node there is a list of things that are dispatched but haven't reported back yet, so node loops over the list again and again checking to see if they are finished. After they finished they get 'processed', e.g. any callbacks that depended on them finishing will get invoked. 170 | 171 | Here is a pseudocode version of the above example: 172 | 173 | ```js 174 | function addOne(thenRunThisFunction) { 175 | waitAMinuteAsync(function waitedAMinute() { 176 | thenRunThisFunction() 177 | }) 178 | } 179 | 180 | addOne(function thisGetsRunAfterAddOneFinishes() {}) 181 | ``` 182 | 183 | Imagine you had 3 async functions `a`, `b` and `c`. Each one takes 1 minute to run and after it finishes it calls a callback (that gets passed in the first argument). If you wanted to tell node 'start running a, then run b after a finishes, and then run c after b finishes' it would look like this: 184 | 185 | ```js 186 | a(function() { 187 | b(function() { 188 | c() 189 | }) 190 | }) 191 | ``` 192 | 193 | When this code gets executed, `a` will immediately start running, then a minute later it will finish and call `b`, then a minute later it will finish and call `c` and finally 3 minutes later node will stop running since there would be nothing more to do. There are definitely more elegant ways to write the above example, but the point is that if you have code that has to wait for some other async code to finish then you express that dependency by putting your code in functions that get passed around as callbacks. 194 | 195 | The design of node requires you to think non-linearly. Consider this list of operations: 196 | 197 | ``` 198 | read a file 199 | process that file 200 | ``` 201 | 202 | If you were to naively turn this into pseudocode you would end up with this: 203 | 204 | ``` 205 | var file = readFile() 206 | processFile(file) 207 | ``` 208 | 209 | This kind of linear (step-by-step, in order) code is isn't the way that node works. If this code were to get executed then `readFile` and `processFile` would both get executed at the same exact time. This doesn't make sense since `readFile` will take a while to complete. Instead you need to express that `processFile` depends on `readFile` finishing. This is exactly what callbacks are for! And because of the way that JavaScript works you can write this dependency many different ways: 210 | 211 | ```js 212 | var fs = require('fs') 213 | fs.readFile('movie.mp4', finishedReading) 214 | 215 | function finishedReading(error, movieData) { 216 | if (error) return console.error(error) 217 | // do something with the movieData 218 | } 219 | ``` 220 | 221 | But you could also structure your code like this and it would still work: 222 | 223 | ```js 224 | var fs = require('fs') 225 | 226 | function finishedReading(error, movieData) { 227 | if (error) return console.error(error) 228 | // do something with the movieData 229 | } 230 | 231 | fs.readFile('movie.mp4', finishedReading) 232 | ``` 233 | 234 | Or even like this: 235 | 236 | ```js 237 | var fs = require('fs') 238 | 239 | fs.readFile('movie.mp4', function finishedReading(error, movieData) { 240 | if (error) return console.error(error) 241 | // do something with the movieData 242 | }) 243 | ``` 244 | 245 | ## Events 246 | 247 | In node if you require the [events](http://nodejs.org/api/events.html) module you can use the so-called 'event emitter' that node itself uses for all of its APIs that emit things. 248 | 249 | Events are a common pattern in programming, known more widely as the ['observer pattern'](http://en.wikipedia.org/wiki/Observer_pattern) or 'pub/sub' (publish/subscribe). Whereas callbacks are a one-to-one relationship between the thing waiting for the callback and the thing calling the callback, events are the same exact pattern except with a many-to-many API. 250 | 251 | Here are few common use cases for using events instead of plain callbacks: 252 | 253 | - Chat room where you want to broadcast messages to many listeners 254 | - Game server that needs to know when new players connect, disconnect, move, shoot and jump 255 | - Database connector that might need to know when the database connection opens, closes or sends an error 256 | 257 | If we were trying to write a module that connects to a chat server using only callbacks it would look like this: 258 | 259 | ```js 260 | var chatClient = require('my-chat-client') 261 | 262 | function onConnect() { 263 | // have the UI show we are connected 264 | } 265 | 266 | function onConnectionError(error) { 267 | // show error to the user 268 | } 269 | 270 | function onDisconnect() { 271 | // tell user that they have been disconnected 272 | } 273 | 274 | function onMessage(message) { 275 | // show the chat room message in the UI 276 | } 277 | 278 | chatClient.connect( 279 | 'http://mychatserver.com', 280 | onConnect, 281 | onConnectionError, 282 | onDisconnect, 283 | onMessage 284 | ) 285 | ``` 286 | 287 | As you can see this is really cumbersome because of all of the functions that you have to pass in a specific order to the `.connect` function. Writing this with events would look like this: 288 | 289 | ```js 290 | var chatClient = require('my-chat-client').connect() 291 | 292 | chatClient.on('connect', function() { 293 | // have the UI show we are connected 294 | }) 295 | 296 | chatClient.on('connectionError', function() { 297 | // show error to the user 298 | }) 299 | 300 | chatClient.on('disconnect', function() { 301 | // tell user that they have been disconnected 302 | }) 303 | 304 | chatClient.on('message', function() { 305 | // show the chat room message in the UI 306 | }) 307 | ``` 308 | 309 | This approach is similar to the pure-callback approach but introduces the `.on` method, which subscribes a callback to an event. This means you can choose which events you want to subscribe to from the `chatClient`. You can also subscribe to the same event multiple times with different callbacks: 310 | 311 | ```js 312 | var chatClient = require('my-chat-client').connect() 313 | chatClient.on('message', logMessage) 314 | chatClient.on('message', storeMessage) 315 | 316 | function logMessage(message) { 317 | console.log(message) 318 | } 319 | 320 | function storeMessage(message) { 321 | myDatabase.save(message) 322 | } 323 | ``` 324 | 325 | MORE EVENTS CONTENT TODO 326 | 327 | ## Streams 328 | 329 | Early on in the project the file system and network APIs had their own separate patterns for dealing with streaming I/O. For example, files in a file system have things called 'file descriptors' so the `fs` module had to have extra logic to keep track of these things whereas the network modules didn't have such a concept. Despite minor differences in semantics like these, at a fundamental level both groups of code were duplicating a lot of functionality when it came to reading data in and out. The team working on node realized that it would be confusing to have to learn two sets of semantics to essentially do the same thing so they made a new API called the `Stream` and made all the network and file system code use it. 330 | 331 | The whole point of node is to make it easy to deal with file systems and networks so it made sense to have one pattern that was used everywhere. The good news is that most of the patterns like these (there are only a few anyway) have been figured out at this point and it is very unlikely that node will change that much in the future. 332 | 333 | THE REST IS TODO, in the meantime read the [streams handbook](https://github.com/substack/stream-handbook#introduction) 334 | 335 | ## Modules 336 | 337 | TODO 338 | 339 | ## Going with the grain 340 | 341 | Like any good tool, node is best suited for a certain set of use cases. For example: Rails, the popular web framework, is great for modeling complex [business logic](http://en.wikipedia.org/wiki/Business_logic), e.g. using code to represent real life business objects like accounts, loan, itineraries, and inventories. While it is technically possible to do the same type of thing using node, there would be definite drawbacks since node is designed for solving I/O problems and it doesn't know much about 'business logic'. Each tool focuses on different problems. Hopefully this guide will help you gain an intuitive understanding of the strengths of node so that you know when it can be useful to you. 342 | 343 | ### What is outside of node's scope? 344 | 345 | Fundamentally node is just a tool used for managing I/O across file systems and networks, and it leaves other more fancy functionality up to third party modules. Here are some things that are outside the scope of node: 346 | 347 | #### Web frameworks 348 | 349 | There are a number of web frameworks built on top of node (framework meaning a bundle of solutions that attempts to address some high level problem like modeling business logic), but node is not a web framework. Web frameworks that are written using node don't always make the same kind of decisions about adding complexity, abstractions and tradeoffs that node does and may have other priorities. 350 | 351 | #### Language syntax 352 | 353 | Node uses JavaScript and doesn't change anything about it. Felix Geisendörfer has a pretty good write-up of the 'node style' [here](https://github.com/felixge/node-style-guide). 354 | 355 | #### Language abstraction 356 | 357 | When possible node will use the simplest possible way of accomplishing something. The 'fancier' you make your JavaScript the more complexity and tradeoffs you introduce. Programming is hard, especially in JS where there are 1000 solutions to every problem! It is for this reason that node tries to always pick the simplest, most universal option. If you are solving a problem that calls for a complex solution and you are unsatisfied with the 'vanilla JS solutions' that node implements, you are free to solve it inside your app or module using whichever abstractions you prefer. 358 | 359 | A great example of this is node's use of callbacks. Early on node experimented with a feature called 'promises' that added a number of features to make async code appear more linear. It was taken out of node core for a few reasons: 360 | 361 | - they are more complex than callbacks 362 | - they can be implemented in userland (distributed on npm as third party modules) 363 | 364 | Consider one of the most universal and basic things that node does: reading a file. When you read a file you want to know when errors happen, like when your hard drive dies in the middle of your read. If node had promises everyone would have to branch their code like this: 365 | 366 | ```js 367 | fs.readFile('movie.mp4') 368 | .then(function(data) { 369 | // do stuff with data 370 | }) 371 | .error(function(error) { 372 | // handle error 373 | }) 374 | ``` 375 | 376 | This adds complexity, and not everyone wants that. Instead of two separate functions node just uses a single callback function. Here are the rules: 377 | 378 | - When there is no error pass null as the first argument 379 | - When there is an error, pass it as the first argument 380 | - The rest of the arguments can be used for anything (usually data or responses since most stuff in node is reading or writing things) 381 | 382 | Hence, the node callback style: 383 | 384 | ```js 385 | fs.readFile('movie.mp4', function(err, data) { 386 | // handle error, do stuff with data 387 | }) 388 | ``` 389 | 390 | #### Threads/fibers/non-event-based concurrency solutions 391 | 392 | Note: If you don't know what these things mean then you will likely have an easier time learning node, since unlearning things is just as much work as learning things. 393 | 394 | Node uses threads internally to make things fast but doesn't expose them to the user. If you are a technical user wondering why node is designed this way then you should 100% read about [the design of libuv](http://nikhilm.github.com/uvbook/), the C++ I/O layer that node is built on top of. 395 | 396 | ## Real-time apps 397 | 398 | TODO - this section will have a non-contrived, functioning application with a web UI whose architecture will be dissected and discussed. 399 | 400 | ## License 401 | 402 | ![CCBY](CCBY.png) 403 | 404 | Creative Commons Attribution License (do whatever, just attribute me) 405 | http://creativecommons.org/licenses/by/2.0/ 406 | 407 | Donate icon is from the [http://thenounproject.com/noun/donate/#icon-No285](Noun Project) 408 | -------------------------------------------------------------------------------- /readme.fr.md: -------------------------------------------------------------------------------- 1 | # The Art of Node 2 | ## Une introduction à Node.js 3 | 4 | Si vous souhaitez découvrir Node et que les connaissances énoncées ci-après vous sont familières, ce document est fait pour vous : 5 | 6 | - Un langage de script, tel que JavaScript, Ruby, Python, Perl, etc. Si vous n'êtes pas programmeur, vous préfèrerez sans doute précéder cette lecture par la découverte de [JavaScript for Cats](http://jsforcats.com/). :cat2: 7 | - Git & Github qui sont les outils de collaboration privilégiés par la communauté. Rassurez-vous, pas besoin d'être expert, quelques connaissances basiques suffiront. Au besoin, voici trois superbes tutos pour bien démarrer avec git : [1](https://github.com/jlord/git-it-electron#readme), [2](http://ericsteinborn.com/github-for-cats/#/), [3](http://opensourcerer.diy.org/) 8 | 9 | Ce court livre est une oeuvre en cours de réalisation. Si vous l'aimez, **donnez-moi un dollar** via 10 | [gittip](https://www.gittip.com/maxogden/) pour me permettre d'y consacrer du temps ! 11 | 12 | [![donate](donate.png)](https://www.gittip.com/maxogden/) 13 | 14 | ## Sommaire 15 | 16 | - [Apprentissage Interactif de Node](#apprentissage-interactif-de-node) 17 | - [Comprendre Node](#comprendre-node) 18 | - [Modules de base](#modules-de-base) 19 | - [Callbacks](#callbacks) 20 | - [Evenements](#evenements) 21 | - [Flux](#flux) 22 | - [Modules & npm](#modules) 23 | - [Développement côté client avec npm](#developpez-cote-client-avec-npm) 24 | - [Suivez le mouvement !](#suivez-le-mouvement) 25 | 26 | ## Apprentissage Interactif de Node 27 | 28 | Par expérience, j'ai appris que la simple lecture d'un guide ne se suffit pas à elle-même. Gardez votre éditeur de texte favoris sous la main et écrivez du node en parallèle ! Apprendre en codant est le meilleur moyen d'intégrer les concepts que vous lirez. 29 | 30 | ### NodeSchool.io 31 | 32 | [NodeSchool.io](http://nodeschool.io/) est une série d'ateliers opensource et gratuits qui vous enseignerons les principes de Node.js, et plus encore pour les curieux ! 33 | 34 | [Learn You The Node.js](https://github.com/rvagg/learnyounode#learn-you-the-nodejs-for-much-win) est le cours introductif aux ateliers NodeSchool.io. Il met en scène les principaux cas d'utilisation de Node.js. Il est conçu pour être utilisé directement en ligne de commande. 35 | 36 | [![learnyounode](https://github.com/rvagg/learnyounode/raw/master/learnyounode.png)](https://github.com/rvagg/learnyounode#learn-you-the-nodejs-for-much-win) 37 | 38 | Installez-le avec npm: 39 | 40 | ``` 41 | # install 42 | npm install learnyounode -g 43 | 44 | # start the menu 45 | learnyounode 46 | ``` 47 | 48 | ## Comprendre Node 49 | 50 | Node.js est un projet opensource conçu pour vous aider à écrire des programmes JavaScript qui interagissent avec des réseaux, des Filesystems ou toute autre source d'I/O (entrée/sortie, lecture/ecriture). Et c'est tout! Node n'est qu'une plateforme simple et stable qui vous encourage à construire des modules par dessus. 51 | 52 | Avec quelques exemples, tout sera plus clair. Ci-après le diagramme d'une application que j'ai réalisé avec Node et qui présente de nombreuses sources d'I/O: 53 | 54 | ![server diagram](server-diagram.png) 55 | 56 | Rassurez-vous, vous n'avez pas besoin de tout comprendre à ce graphe. Le but est de vous montrer qu'un simple processus node (l'hexagone au centre) peut agir comme un hub entre les différentes sources d'I/O (en orange et violet sur le diagramme). 57 | 58 | Usuellement, produire ce type de système induit deux conséquences probables: 59 | 60 | - D'excellentes performances a l'exécution, mais aux prix de difficultés dans l'écriture (comme partir de zéro pour écrire un serveur web en C) 61 | - Une simplicité d'écriture, mais de faibles performances, ou un manque de robustesse (comme quand quelqu'un essaye d'envoyer un fichier de 5Go sur votre serveur et qu'il crash) 62 | 63 | L'objectif poursuivit par Node est de trouver l'équilibre entre ces deux situations : Être accessible tout en offrant des performances optimales. 64 | 65 | Attention, Node n'est ni : 66 | 67 | - Un framework web (comme Rails ou Django, même s'il peut être utilisé pour produire ce genre de choses) 68 | - Un langage de programmation (Il est basé sur JavaScript mais node n'est pas son propre langage) 69 | 70 | Au lieu de cela, Node se situe quelque part au milieu. On peut dire qu'il est à la fois : 71 | 72 | - Conçu pour être simple et donc relative facile à comprendre et utiliser 73 | - Adapté aux programmes fondés sur des I/O, nécessitants rapidité et capacité à gérer de nombreuses connections 74 | 75 | A bas niveau, Node peut se décrire comme un outil permettant l'écriture de deux types de programmes majeurs : 76 | 77 | - Les programmes de Réseaux qui utilisent les protocoles du web: HTTP, TCP, UDP, DNS and SSL 78 | - Les programmes qui lisent et écrivent des données dans les filesystem ou dans les processus locaux ou en mémoire. 79 | 80 | Qu'est-ce qu'un programme "fondé sur des I/O" ? Voici quelques exemples de sources: 81 | 82 | - Bases de données (e.g. MySQL, PostgreSQL, MongoDB, Redis, CouchDB) 83 | - APIs (e.g. Twitter, Facebook, Notifications Push Apple) 84 | - HTTP/connections WebSocket (des utilisateurs d'une application web) 85 | - Fichiers (redimensionnement d'images, editeur video, radio internet) 86 | 87 | Node gère les I/O de manière [asynchrone](http://en.wikipedia.org/wiki/Asynchronous_I/O) ce qui le rend très efficace dans la gestion de processus simultanés. Prenons un exemple: si vous commander un cheeseburger dans un fast-food, ils prendront votre commande et vous feront patienter le temps que votre plat soit prêt. Pendant ce temps, ils prendront les commandes des autres clients et n'auront aucun mal à démarrer la cuissons de leur cheeseburger en parralèle. Maintenant imaginez un peu si vous deviez attendre votre sandwich au comptoire, empêchant tous les clients derrière vous de commander jusqu'à ce que votre produit soit prêt ! On appelle cela l'**I/O Bloquante** car toutes les I/O se produisent l'une après l'autre. Node, à contrario, est **non-bloquante**, ce qui signifie qu'il peut cuire plusieurs cheeseburgers à la fois ! 88 | 89 | Quelques exemples amusants de choses permises par la nature non-bloquante de Node : 90 | 91 | - Contrôler des [quadricopters volants](http://nodecopter.com) 92 | - Ecrire des bots IRC 93 | - Créer des [robots marcheurs bipèdes](http://www.youtube.com/watch?v=jf-cEB3U2UQ) 94 | 95 | ## Modules de base 96 | 97 | Pour commencer, je vous suggère d'installer Node sur votre machine. Le plus simple est de vous rendre sur [nodejs.org](http://nodejs.org) et de cliquer sur 'Install'. 98 | 99 | Node possède nativement un petit groupe de modules (qui réponds communément au nom de 'Node core' - 'Coeur de Node') qui sont présenté en tant qu'API publique, et avec lesquels nous sommes censés écrire nos programmes. Pour travailler avec un file system, il y a le module 'fs', et pour les réseaux, les modules comme `net` (TCP), `http`, `dgram` (UDP). 100 | 101 | En sus de `fs` et des modules de réseau, le Coeur de Node propose d'autres modules de base. Il existe un module pour gérer les requêtesDNS de manière asynchrones nommé `dns`, un autre pour récupérer les informations spécifiques à l'OS comme la path du tmpdir nommé `os`, encore un pour allouer des morceaux de mémoire nommé `buffer`, d'autres pour parser les url et les paths (`url`, `querystring`, `path`), etc. La plus part, sinon tous, sont là pour gérer le principal cas d'utilisation de node: Ecrire rapidement des programmes qui parlent aux files systems et aux réseaux. 102 | 103 | Node possède plusieurs cordes à son arc pour gérer les 'I/O: des callbacks, des évènements, des streams - 'flux' et des modules. Si vous arrivez à apprendre comment ces quatre structures fonctionnent, alors vous serez capable d'aller dans n'importe lequel des module core de Node, et de comprendre comment vous interfacer avec eux. 104 | 105 | ## Callbacks 106 | 107 | Voilà le sujet le plus important si vous voulez comprendre comment utiliser node. On retrouve les callbacks à peu près partout dans Node. Ils n'ont pas été inventés par node cependant, ils font simplement parti intégrante de JavaScript. 108 | 109 | Les Callbacks sont des fonctions qui s'exécutent de manière asynchrone ou plus tard dans le temps. Au lieu de lire le code de haut en bas de manière procédurale, les programmes asynchrones peuvent exécuter différentes fonctions à différents moments. Cet ordre sera définit en fonction de l'ordre et de la vitesse des précédents appels, comme les requetes HTTP ou bien encore la lecture du file system. 110 | 111 | Cette différence peut entrainer des confusions, car déterminer si une fonction est asynchrone our non dépend beaucoup de son contexte. Voici un exemple synchrone, ce qui signifie que que vous pouvez lire ce code de haut en bas comme un livre: 112 | 113 | ```js 114 | var myNumber = 1 115 | function addOne() { myNumber++ } // define the function 116 | addOne() // run the function 117 | console.log(myNumber) // logs out 2 118 | ``` 119 | 120 | Ce code définit une fonction, puis appel cette fonction, sans attendre quoi que ce soit. Quand la fonction est appelé, elle ajoute immédiatement 1 aux nombre. On peut donc s'attendre à ce qu'après l'appel de cette fonction, le nombre soit égal à 2. De manière assez basique donc, le code synchrone s'exécute de haut en bas. 121 | 122 | Node en revanche, utilise essentiellement du code asynchrone. Utilisons Node pour lire notre nombre depuis un fichier appelé `number.txt`: 123 | 124 | ```js 125 | var fs = require('fs') // require is a special function provided by node 126 | var myNumber = undefined // we don't know what the number is yet since it is stored in a file 127 | 128 | function addOne() { 129 | fs.readFile('number.txt', function doneReading(err, fileContents) { 130 | myNumber = parseInt(fileContents) 131 | myNumber++ 132 | }) 133 | } 134 | 135 | addOne() 136 | 137 | console.log(myNumber) // logs out undefined -- this line gets run before readFile is done 138 | ``` 139 | 140 | Pourquoi obtenons nous `undefined` quand nous affichons le chiffre cette fois ci ? Dans ce code, nous utilisons la méthode`fs.readFile`, qui est une méthode asynchrone. Tout ce qui doit parler à un disque dur ou à un réseau aura tendance à être asynchrone. Si leur objectif est simplement d'accéder à la mémoire, ou travailler avec le processeur, alors ils seront synchrone. La raison est que l'I/O est effroyablement lent! En guise d'illustration, dites vous que parler avec un disque dur est environ 100,000 fois plus lent qu'une communication avec la mémoire(e.g. RAM). 141 | 142 | Quand nous lançons ce programme, toutes ses fonctions sont immédiatement définies, mais elles n'ont pas besoin de s'exécuter immédiatement. C'est un élément fondamental à comprendre en programmation asynchrone. Quand `addOne` est appelé, il démarre `readFile` et enchaine avec le prochain élément prêt à être exécuté. S'il n'y a rien dans la file d'attente, Node attendra les opérations fs/réseau en attente pour terminer, ou il s'arrêtera simplement de tourner et sortira sur la ligne de commande. 143 | 144 | Quand `readFile` aura terminé de lire le fichier (cela peut prendre entre quelques millisecondes et plusieurs minutes, en fonction de la vitesse du disque dur) il lancera la fonction `doneReading`, puis lui donnera une erreur (s'il y en a une) ainsi que le contenu du fichier. 145 | 146 | La raison pour laquelle nous obtenons `undefined` ci-dessus est qu'il n'existe aucune logique dans notre code pour dire à notre `console.log` d'attendre que le `readFile` ait terminé avant de sortir notre chiffre. 147 | 148 | Si vous avez du code que vous voulez pouvoir exécuter encore et encore, ou simplement plus tard, la première étape consiste à encapsuler ce code dans une fonction. Ensuite, vous pouvez indiquer à votre fonction le moment où elle devra l'exécuter. Bien évidemment, donner des noms descriptifs et verbeux à vos fonction aidera grandement. 149 | 150 | Les Callbacks ne sont que des fonctions qui s'exécutent plus tard. La clef pour comprendre les callbacks est de réaliser que vous ne savez pas **quand** une opération asynchrone sera terminée, mais vous savez **où** cette opération doit se compléter - la dernière ligne de votre fonction asynchrone ! L'ordre haut-en-bas de déclaration de vos callbacks n'a pas d'importance, seul l'encapsulation logique compte. Commencez par découper votre code en fonction, puis utilisez vos callbacks pour déclarer qu'une fonction requiert qu'une autre se termine. 151 | 152 | La méthode `fs.readFile` fournie par node est asynchrone et il se trouve qu'elle prend beaucoup de temps pour se terminer. Mettez-vous à sa place: elle doit aller interroger l'OS, qui à son tour doit se renseigner auprès du file système, qui vit sur le disque dur, qui peut ne pas en trainer de tourner à des miliers de tours par minute. Ensuite il doit utiliser un laser pour lire une donnée, puis la renvoyer à travers toutes les strates successives de votre programme JavaScript. Vous donnez donc à `readFile` une fonction (aussi appelé callback) qu'il appellera une fois qu'il aura récupéré les données de votre file system. Il placera les données qu'il a récupéré dans une variable JavaScript et appellera votre callback avec cette variable. Dans ce cas, la variable est nommé `fileContents` car elle détient le contenu du fichier qui a été lu. 153 | 154 | Reprenez l'exemple du restaurant cité au début de ce tuto. Très souvent, vous trouverez dans les restaurant des numéros à poser sur votre table pendant que vous patientez. Ces numéros sont comme des callbacks. Ils indiquent au serveur ce qu'ils doivent faire une fois que votre sandwich est prêt. 155 | 156 | Plaçons maintenant notre `console.log` dans une fonction et passons le en callback. 157 | 158 | ```js 159 | var fs = require('fs') 160 | var myNumber = undefined 161 | 162 | function addOne(callback) { 163 | fs.readFile('number.txt', function doneReading(err, fileContents) { 164 | myNumber = parseInt(fileContents) 165 | myNumber++ 166 | callback() 167 | }) 168 | } 169 | 170 | function logMyNumber() { 171 | console.log(myNumber) 172 | } 173 | 174 | addOne(logMyNumber) 175 | ``` 176 | 177 | La fonction `logMyNumber` peut désormais être passée en argument qui deviendra la variable de `callback` dans la fonction `addOne`. Une fois que `readFile` en a terminé, la variable `callback` sera invoquée (`callback()`). Seules les fonctions peuvent être invoquées, donc si vous passez n'importe quoi d'autre qu'une fonction, il en résultera une erreur. 178 | 179 | Quand une fonction est invoquée en JavaScript, le code qu'elle renferme est immédiatement exécuté. Dans notre cas, notre console log s'exécutera puisque `callback` est `logMyNumber`. Rappelez-vous, le simple fait de *define* une fonction ne signifie pas qu'elle s'exécutera. Pour ce faire, vous devez *invoke* une fonction. 180 | 181 | Pour aller encore plus loin avec cet exemple, voici une liste chronique des évènements qui se produisent à l'éxécution de ce code: 182 | 183 | - 1: Le code est parsé, ce qui signifique qu'une quelconque erreur syntaxique casserait le programme. Durant cette phase initiale, il y a 4 choses qui sont définies: `fs`, `myNumber`, `addOne`, and `logMyNumber`. Notez qu'elles sont simplement définies. Aucune fonction n'a encore été invoquée ou appelée pour le moment. 184 | - 2: Quand la dernière ligne de notre programme est exécutée `addOne` est invoqué, puis est passé dans la fonction `logMyNumber` comme 'callback', ce qui est bien ce que nous demandons quand `addOne` est terminé. Cela entraine immédiatement le démarrage de la fonction asynchrone `fs.readFile`. Cette partie du programme prend un peu de temps à se terminer. 185 | - 3: Puisqu'il n'a rien à faire, Node patiente pendant que `readFile` se termine. S'il y avait une quelconque autre tache à réaliser, Node serait disponible pour faire le boulot. 186 | - 4: `readFile` se termine et appelle son callback, `doneReading`, qui à son tour incrémente le nombre et invoque immédiatement la fonction qu'`addOne` a passé, `logMyNumber` (son callback). 187 | 188 | La chose la plus troublante quand on programme avec des callbacks est probablement le fait que les fonctions sont de simples objets, encapsulables dans des variables et que l'on peut passer n'importe où avec des noms différents. Affecter des noms simples et descriptifs à vos variables est primordiale pour rendre votre code lisible pour les autres comme pour vous-même. D'une manière générale, si vous voyez une variable comme `callback` ou `cb` vous pouvez partir du principe qu'il s'agit d'une fonction. 189 | 190 | Vous avez peut-être entendu les termes de 'evented programming' (programmation évènementielle) ou 'event loop' (boucle d'évènement). Ils se réfèrent à la manière dont `readFile` est implémenté. Node dispatch d'abord les opérations `readFile` puis attends que `readFile` envoi un évènement qu'il a clôturé. 191 | Pendant qu'il patiente, Node peut tranquillement s'affairer ailleurs. Pour s'y retrouver, Node maintient une liste de tâches qui ont été dispatchées mais qui n'ont pas encore reçu de feedback, et boucle dessus indéfiniment jusqu'à ce que l'une d'entre elle soit terminée. Une fois chose faite, Node invoque les éventuels callbacks qui lui sont rattachés. 192 | 193 | Voila une version de l'exemple précédent en pseudocode: 194 | 195 | ```js 196 | function addOne(thenRunThisFunction) { 197 | waitAMinuteAsync(function waitedAMinute() { 198 | thenRunThisFunction() 199 | }) 200 | } 201 | 202 | addOne(function thisGetsRunAfterAddOneFinishes() {}) 203 | ``` 204 | 205 | Imaginez que vous avez 3 fonctions asynchrones `a`, `b` et `c`. Chacune d'entre elle prend environ 1 minute a être complétée puis lance un callback (qui se voit passé en premier argument). Si vous vouliez dire à node 'lance a, puis b une fois que a est terminé, puis c une fois que b est terminé', cela ressemblerait à cela : 206 | 207 | ```js 208 | a(function() { 209 | b(function() { 210 | c() 211 | }) 212 | }) 213 | ``` 214 | Quand ce code est exécuté, `a` démarrera immédiatement, puis une minute plus tard il appellera `b`, qui une minute plus tard lancera `c`. Au bout de 3 minutes, node s'arrêtera puisqu'il n'y aura plus rien à faire. Bien évidemment, il existe des méthodes plus élégantes pour écrire le code ci-dessus, mais le but est de montrer que si vous avez du code qui doit attendre un autre code asynchrone pour s'exécuter, alors il faut exprimer cette dépendance en disposant votre code dans une fonction qui sera alors passé comme callback. 215 | 216 | Le design de node requière un mode de pensé non-linéaire. Considérez donc cette liste d'opérations: 217 | 218 | ``` 219 | read a file 220 | process that file 221 | ``` 222 | 223 | Si vous transformiez cela en pseudocode vous obtiendriez ceci: 224 | 225 | ``` 226 | var file = readFile() 227 | processFile(file) 228 | ``` 229 | 230 | Ce type de code non-linéaire (étape par étape, dans l'ordre) n'est pas la manière dont Node fonctionne. Si ce code devait être exécuté, alors `readFile` et `processFile` devraient être lancés au même moment. Cela n'aurait aucun sens puisque `readFile` mettra du temps à se terminer. A la place, vous devez signifier que `processFile` dépend de `readFile`. C'est exactement à cela que servent les callbacks ! Et parce que javascript fonctionne ainsi, vous pourrez écrire cette dépendance de plusieurs manières: 231 | 232 | ```js 233 | var fs = require('fs') 234 | fs.readFile('movie.mp4', finishedReading) 235 | 236 | function finishedReading(error, movieData) { 237 | if (error) return console.error(error) 238 | // do something with the movieData 239 | } 240 | ``` 241 | 242 | Mais vous pourriez aussi structurer votre code de cette façon et il fonctionnerait toujours: 243 | 244 | ```js 245 | var fs = require('fs') 246 | 247 | function finishedReading(error, movieData) { 248 | if (error) return console.error(error) 249 | // do something with the movieData 250 | } 251 | 252 | fs.readFile('movie.mp4', finishedReading) 253 | ``` 254 | 255 | Ou même comme ceci: 256 | 257 | ```js 258 | var fs = require('fs') 259 | 260 | fs.readFile('movie.mp4', function finishedReading(error, movieData) { 261 | if (error) return console.error(error) 262 | // do something with the movieData 263 | }) 264 | ``` 265 | 266 | ## Evenements 267 | Dans le cas où vous auriez besoin du module d'[evenements](http://nodejs.org/api/events.html), node vous propose 'event emitter', un module utilisé nativement par Node pour l'ensemble des ses API crée. 268 | 269 | L'utilisation d'évènements est chose très commune en programmation, plus connu en tant que patron de conception ['Observation'](https://fr.wikipedia.org/wiki/Observateur_(patron_de_conception)) ou encore 'Observateur/Observable'. Tandis que les callbacks sont des relation one-to-one entre la chose qui attend le callback et celle qui appelle ce callback, les évènements répondent au même schema, a l'exception de leur système relationnel many-to-many. 270 | 271 | La manière la plus simple d'imaginer les évènements est de considérer le fait qu'ils vous permettent de vous abonner à quelque chose. Vous pouvez dire : 'Quand X, fait Y', alors qu'un callback fonctionnera en 'Fait X puis Y'. 272 | 273 | Ci-après une liste de cas d'utilisation où l'on privilégiera les évènements sur l'utilisation de callbacks: 274 | 275 | - Canaux de discussion pour envoyer un message à un grand nombre d'observateurs 276 | - Serveurs de jeux qui nécessitent de savoir quand de nouveaux joueurs se connectent, déconnectent, se déplacent, sautent ou tirent 277 | - Moteur de jeu où vous voulez permettre aux développeurs de souscrire à des évènements comme `.on('jump', function() {})` 278 | - Server web bas niveau où l'on veut exposer une API pour facilement accrocher des evenements comme `.on('incomingRequest')` or `.on('serverError')` 279 | 280 | Si nous voulions écrire un module qui se connecte à un serveur de chat en utilisant uniquement des callbacks, cela ressemblerait à cela : 281 | 282 | ```js 283 | var chatClient = require('my-chat-client') 284 | 285 | function onConnect() { 286 | // have the UI show we are connected 287 | } 288 | 289 | function onConnectionError(error) { 290 | // show error to the user 291 | } 292 | 293 | function onDisconnect() { 294 | // tell user that they have been disconnected 295 | } 296 | 297 | function onMessage(message) { 298 | // show the chat room message in the UI 299 | } 300 | 301 | chatClient.connect( 302 | 'http://mychatserver.com', 303 | onConnect, 304 | onConnectionError, 305 | onDisconnect, 306 | onMessage 307 | ) 308 | ``` 309 | 310 | Comme vous pouvez le constater, cette méthode est particulièrement lourde, car il faut passer toutes les fonctions dans un ordre spécifique à la fonction `.connect`. 311 | Avec une écriture évènementielle, nous obtiendriont ceci: 312 | 313 | ```js 314 | var chatClient = require('my-chat-client').connect() 315 | 316 | chatClient.on('connect', function() { 317 | // have the UI show we are connected 318 | }) 319 | 320 | chatClient.on('connectionError', function() { 321 | // show error to the user 322 | }) 323 | 324 | chatClient.on('disconnect', function() { 325 | // tell user that they have been disconnected 326 | }) 327 | 328 | chatClient.on('message', function() { 329 | // show the chat room message in the UI 330 | }) 331 | ``` 332 | 333 | L'approche est similaire à la version en Callbacks, mais introduit les méthodes `.on`, qui rattachent des callbacks à un évènement. Ce qui signifie que vous pouvez choisir à quel évènement vous voulez souscrire depuis le `chatClient`. Vous pouvez aussi écouter le même évènement à de multiple reprises avec différents callbacks : 334 | 335 | ```js 336 | var chatClient = require('my-chat-client').connect() 337 | chatClient.on('message', logMessage) 338 | chatClient.on('message', storeMessage) 339 | 340 | function logMessage(message) { 341 | console.log(message) 342 | } 343 | 344 | function storeMessage(message) { 345 | myDatabase.save(message) 346 | } 347 | ``` 348 | 349 | ## Flux 350 | 351 | Plus tôt dans le projet node, le file system et les APIs de réseaux avaient leurs schemas de fonctionnement séparés pour gérer les flux d'I/O. Par exemple, les fichiers du file system avaient des 'file descriptors', le module `fs` nécessitaient de la logique supplémentaire pour garder des traces de toutes ces choses, tandis que les modules de réseau ignoraient ces concepts. En dépit de différences sémantiques mineurs comme celles ci, au niveau fondamental, les deux groupes de code duplicaient beaucoup de fonctionnalités quand il s'agissait de lire les données en entrée et en sortir. Les équipes développant node on réalisé qu'il serait confus d'avoir à apprendre deux groupes sémantiques pour faire relativement la même chose, ils ont alors créée une nouvelle API nommé `Stream` à la fois pour le File system et pour le réseau. 352 | 353 | Tout l'intéret de node réside dans sa capacité à faciliter l'interaction avec les file system et les réseaux, il était donc sensé d'avoir un seul schema de fonctionnement valable pour toutes les situations. La bonne nouvelle est que la plus part des cas d'utilisation (et il sont peu nombreux quoi qu'il en soit) on été couvert par node, et il est fort peu probable que node évolue de ce côté à l'avenir. 354 | 355 | Il y a deux ressources formidables dont vous pouvez commencer à apprendre l'utilisation des flux node. La première est stream-adventure (cf. Apprentissage de Node Interactif), et la seconde s'appelle Stream Handbook. 356 | 357 | ### Stream Handbook 358 | 359 | [stream-handbook](https://github.com/substack/stream-handbook#introduction) est un guide, similaire à celui ci, qui contient des références sur absolument tout ce que vous pouvez avoir besoin de savoir sur les flux. 360 | 361 | [![stream-handbook](stream-handbook.png)](https://github.com/substack/stream-handbook) 362 | 363 | ## Modules 364 | 365 | Le Coeur de Node est composé d'une douzaine de modules. Certains bas niveau comme `events` ou `flux`, d'autres plus haut niveau comme `http` et `crypto`. 366 | 367 | Ce design est intentionnel. Node core est supposé être léger, et les modules core doivent être réservés à fournir les outils nécessaires au traitement usuel des protocols I/O, de manière cross-platform. 368 | 369 | Pour tout le reste, il a [npm](https://npmjs.org/). Tout le monde peut y créer de nouveaux modules qui ajoutront quelqueonque fonctionnalité et la publier sur npm. Au moment ou je vous écris ces lignes, il y a 34,000 modules sur npm. 370 | 371 | ### Comment trouver un module ? 372 | 373 | Imaginez que vous souhaitez convertir un fichier PDF en TXT. Le meilleur moyen est de commencer par chercher `npm search pdf`: 374 | 375 | ![pdfsearch](npm-search.png) 376 | 377 | Et il y a des tonnes de résultats! npm est relativement populaire, et vous trouverez généralement de multiples solutions potentielles pour vos besoins. Si vous filtrez suffisamment bien vos résultats vous devriez vous retrouver avec ceci : 378 | 379 | - [hummus](https://github.com/galkahana/HummusJS/wiki/Features) - c++ pdf manipulator 380 | - [mimeograph](https://github.com/steelThread/mimeograph) - api on a conglomeration of tools (poppler, tesseract, imagemagick etc) 381 | - [pdftotextjs](https://npmjs.org/package/pdftotextjs) - wrapper around [pdftotext](https://en.wikipedia.org/wiki/Pdftotext) 382 | - [pdf-text-extract](https://npmjs.org/package/pdf-text-extract) - another wrapper around pdftotext 383 | - [pdf-extract](https://npmjs.org/package/pdf-extract) - wrapper around pdftotext, pdftk, tesseract, ghostscript 384 | - [pdfutils](https://npmjs.org/package/pdfutils) - poppler wrapper 385 | - [scissors](https://npmjs.org/package/scissors) - pdftk, ghostscript wrapper w/ high level api 386 | - [textract](https://npmjs.org/package/textract) - pdftotext wrapper 387 | - [pdfiijs](https://github.com/fagbokforlaget/pdfiijs) - pdf to inverted index using textiijs and poppler 388 | - [pdf2json](https://github.com/modesty/pdf2json/blob/master/readme.md) - pure js pdf to json 389 | 390 | Beaucoup de ces modules ont des fonctionnalités similaires, mais présentent des APIs alternatives. Ils requièrent aussi très souvent des dépendances externes comme (`apt-get install poppler`). 391 | 392 | Voici une approche pour interpréter ces différents modules: 393 | 394 | - `pdf2json` est le seul rédigé en pure JavaScript, ce qui signifie qu'il est aussi le plus simple à installer, notamment sur des petites configurations comme un raspberry pi ou un Windows ou le code natif n'est pas nécessairement cross platform. 395 | - Les modules comme `mimeograph`, `hummus` et `pdf-extract` combinent chacuns de multiples modules bas niveau pour exposer une API haut niveau. 396 | - Beaucoup de ces modules semblent reposer sur les outils de commande unix `pdftotext`/`poppler`. 397 | 398 | Comparons les différences entre `pdftotextjs` et `pdf-text-extract`, tous deux étant fondées sur l'utilitaire `pdftotext`. 399 | 400 | ![pdf-modules](pdf-modules.png) 401 | 402 | Tout deux possèdent : 403 | 404 | - Des updates récentes 405 | - Des liens vers vos repos Github (indispensable!) 406 | - READMEs 407 | - Une bonne popularité 408 | - Sont sous license libre (tout le monde peut les utiliser librement) 409 | 410 | 411 | En ne regardant que le `package.json` et les statistiques du module, il est difficile de se faire une bonne idée du meilleur choix possible. Comparons les READMEs: 412 | 413 | ![pdf-readmes](pdf-readmes.png) 414 | 415 | Les deux possèdent des descriptions simples, des instructions d'installation, des badges CI, des exemples clairs pour lancer les tests. Fantastique! Mais lequel devons nous utiliser ? Comparons le code: 416 | 417 | ![pdf-code](pdf-code.png) 418 | 419 | `pdftotextjs` contient environs 110 lignes de code, contre 40 pour `pdf-text-extract`, mais les deux peuvent essentiellement se réduire à cette ligne : 420 | 421 | ``` 422 | var child = shell.exec('pdftotext ' + self.options.additional.join(' ')); 423 | ``` 424 | 425 | Est-ce que cela en fait un meilleur que l'autre ? Difficile à dire! Il est indispensable de *lire* le code pour vous faire vos propres conclusions. Si vous trouver un module qui vous plait, lancez `npm star modulename` pour donner votre feedback à npm sur un module qui vous a procuré une experience positive. 426 | 427 | ### Workflow de développement modulaire 428 | 429 | npm diffère de la plupart des gestionnaires de packages par sa capacité à installer des modules des répertoires contenus à l'interieur de modules déja existants. Mêmes si vous n'y voyez pas encore d'intérêt, c'est là la clef du succes d'npm. 430 | 431 | Beaucoup de gestionnaires de packages installent les choses de manière globale. Par exemple, si vous lancez `apt-get install couchdb` sur Debian Linux, il essayera d'installer la dernière version stable de CouchDB. Si vous essayez d'installer CouchDB en tant que dépendance d'un autre logiciel, et que ce logiciel nécessite une version anterieure de CouchDB, vous devrez désinstaller la version la plus récente de CouchDB puis installer la version historique. Vous ne pouvez donc pas avoir deux versions de CouchDB qui coexistent en parralèle car Debian ne sait installer les modules à qu'un endroit. 432 | 433 | Et il n'y a pas que Debian qui fait cela. La plus part des gestionnaires de packages des langages de programmation fonctionnent ainsi. Pour solutionner ce problème, des environnements virtuels ont été développés comme [virtualenv](http://python-guide.readthedocs.org/en/latest/dev/virtualenvs/) pour Python ou [bundler](http://bundler.io/) pour Ruby. Ceux-ci découpent votre environnement en plusieurs environnements virtuels, un pour chaque projet. Toutefois, à l'interieur de chacun de ces environnnements, tout reste installé de manière globale. Les enviornnements virtuels n'adressent donc pas une réponse satisfaisante à notre problème et augmentent qui plus est le niveau de complexité de notre installation. 434 | 435 | Avec npm, l'installation de modules globaux est contre-nature. De la même manière que vous ne devriez pas utilser de variables globales dans vos programmes JavaScript vous ne devriez pas installer de modules globaux (à moins que vous ayez besoin d'un module avec un executable binaire dans votre `PATH` globale, ce qui est loin d'être systématiquement le cas - nous en reparlerons). 436 | 437 | #### Comment fonctionne `require` 438 | 439 | Quand vous faites appel à `require('some_module')` voila ce qui se passe dans node: 440 | 441 | 1. Si un fichier qui s'appelle `some_module.js` existe dans le dossier courant, node le lancera. Autrement: 442 | 2. Node recherchera un dossier nommé `node_modules` dans le repertoire en cours, contenant un fichier `some_module` à l'interieur. 443 | 3. Si il ne trouve toujours pas, il montera d'un niveau et répètera l'opération 2. 444 | 445 | Ce cycle se répètera jusqu'à ce que node atteignent le dossier root du filesystem. A ce moment, il continuera sa recherche dans les repertoires de modules globaux (comme `/usr/local/node_modules` sur Mac OS). Enfin, s'il ne trouve toujours rien, il enverra une erreur. 446 | 447 | Pour illustrer ceci, voila un exemple: 448 | 449 | ![mod-diagram-01](mod-diagram-01.png) 450 | 451 | Quand le dossier de travail en cour est `subsubfolder` et que `require('foo')` est appelé, node va chercher un dossier `subsubsubfolder/node_modules`. Dans le cas présent, sa recherche sera infructeuse, et le répertoire y est nommé par erreur `my_modules`. Node va donc remonter d'un répertoire et recommencer son opération, ce qui signifie qu'il cherchera alors `subfolder_B/node_modules` qui n'existera toujours pas. Enfin, la troisième tentative passera puisque `folder/node_modules` existe bel et bien *et* possède un répertoire nommé `foo` en son sein. Si `foo` n'y était pas, node aurait continué sa recherche jusqu'aux répertoire globaux. 452 | 453 | Notez que s'il est appelé depuis `subfolder_B`, node ne trouvera jamais `subfolder_A/node_modules`, car il ne peut voir `folder/node_modules` que dans sa phase de remontée de l'arborescence. 454 | 455 | Un des atouts de l'approche d'npm est que les modules peuvent installer leurs dépendances à des endroits spécifiques à leur version. Dans ce cas, le module `foo` est plutot populaire - il y en a trois copies, chacune à l'interieur de son dossier parent. Une explication pourrait être que chaque module parent requiert une version différente de `foo`, par exemple 'folder' qui requiert `foo@0.0.1`, `subfolder_A` de `foo@0.2.1` etc. 456 | 457 | Maintenant, voila ce qui se produit si nous corrigeons notre problème de nom en remplaçant `my_modules` par son nom valide `node_modules`: 458 | 459 | ![mod-diagram-02](mod-diagram-02.png) 460 | 461 | Pour tester quel module est véritablement chargé par node, vous pouvez utiliser `require.resolve('some_module')` qui vous montrera la path du module que node trouve dans sa remontée de l'arborescence. `require.resolve` est particulièrement utile pour double-checker que le module que vous *pensez* être chargé l'est *véritablement* -- parfois il y aura une autre version du même module qui sera plus proche de votre repertoire de travail actuel. 462 | 463 | ### Comment écrire un module 464 | 465 | Maintenant que vous savez trouver un module et le `require`, vous pouvez écrire vos propres modules. 466 | 467 | #### Le module le plus simple du monde 468 | 469 | La légèreté des modules de Node est radicale. En voici l'un des plus simple possible : 470 | 471 | `package.json`: 472 | ```js 473 | { 474 | "name": "number-one", 475 | "version": "1.0.0" 476 | } 477 | ``` 478 | 479 | `index.js`: 480 | ```js 481 | module.exports = 1 482 | ``` 483 | 484 | Par défaut, node essaie de lancer `module/index.js` quand il lit `require('module')`. Aucun autre nom de fichier ne fonctionnera à moins que vous definissiez specifiez au champ `main` de `package.json` de pointer dessus. 485 | 486 | Placez les deux fichiers dans un dossier nommé `number-one` (l'`id` dans package.json doit matcher un nom de dossier) et vous aurez un module node fonctionnel. 487 | 488 | En appelant la fonction `require('number-one')` vous retournez la valeur qui est 'set' dans `module.exports` 489 | 490 | ![simple-module](simple-module.png) 491 | 492 | Un moyen encore plus rapide de créer un module serait de lancer les commandes shell suivantes : 493 | 494 | ```sh 495 | mkdir my_module 496 | cd my_module 497 | git init 498 | git remote add git@github.com:yourusername/my_module.git 499 | npm init 500 | ``` 501 | 502 | La commande `npm init` créera automatiquement un `package.json` valide pour vous. Si vous lancer un repo `git` existant, il définiera tout aussi automatiquement `package.json` à l'interieur. 503 | 504 | #### Ajout de dépendances 505 | 506 | Un module peut lister n'importe quel autre module depuis npm our GitHub dans le champ `dépendances` de `package.json`. Pour installer un module `request` en tant que nouvelle dépendance et l'ajouter automatiquement au `package.json`, vous pouvez lancer ceci depuis votre répertoire root : 507 | 508 | ```sh 509 | npm install --save request 510 | ``` 511 | 512 | Cela installera une copie de `request` dans le `node_modules` le plus proche et notre `package.json` ressemblera ainsi à cela: 513 | 514 | ``` 515 | { 516 | "id": "number-one", 517 | "version": "1.0.0", 518 | "dependencies": { 519 | "request": "~2.22.0" 520 | } 521 | } 522 | ``` 523 | 524 | Par défaut `npm install` récupèrera la dernière version publiée du module. 525 | 526 | ## Developpez cote client avec npm 527 | npm est victime d'un vice de pensé assez fréquent. `Node` faisant parti de son nom, il est courant de penser qu'il ne gère que des modules JS côté serveur, ce qui est absolument faux! npm signifie Node Packaged Module, c'est-à-dire des module que Node package pour vous. Ces modules peuvent être n'importe quoi - Ce ne sont que des repertoires ou des fichiers encapsulés dans des .tar.gz et un fichié nommé `package.json` qui explicite la version du module ainsi que la liste de toutes ses dépendances (ainsi que leur propre version de module pour que seules les versions connues pour fonctionner avec notre module ne soient installées automatiquement). Les dépendances ne sont que des modules, qui peuvent eux mêmes avoir des dépendances, et ainsi de suite. 528 | 529 | [browserify](http://browserify.org/) est un utilitaire écrit en node qui tente de traduire n'importe quel module node en code lisible par un browser. Bien que *beaucoup de modules soient compatibles*, tous ne le sont pas (Les browsers ne peuvent par exemple pas heberger un serveur HTTP). 530 | 531 | Pour essayer npm en browser, vous pouvez utiliser [RequireBin](http://requirebin.com/), qui est une application que j'ai réalisé qui tire profit de [Browserify-CDN](https://github.com/jesusabdullah/browserify-cdn). Elle utilise browserify en interne et renvoi l'output à travers HTTP (en lieu et place de la ligne de commande communément utilisé par browserify.) 532 | 533 | Essayez maintenant de mettre ce code dans RequireBin et lancez le boutton de preview: 534 | 535 | ```js 536 | var reverse = require('ascii-art-reverse') 537 | 538 | // makes a visible HTML console 539 | require('console-log').show(true) 540 | 541 | var coolbear = 542 | " ('-^-/') \n" + 543 | " `o__o' ] \n" + 544 | " (_Y_) _/ \n" + 545 | " _..`--'-.`, \n" + 546 | " (__)_,--(__) \n" + 547 | " 7: ; 1 \n" + 548 | " _/,`-.-' : \n" + 549 | " (_,)-~~(_,) \n" 550 | 551 | setInterval(function() { console.log(coolbear) }, 1000) 552 | 553 | setTimeout(function() { 554 | setInterval(function() { console.log(reverse(coolbear)) }, 1000) 555 | }, 500) 556 | ``` 557 | 558 | Ou testez [un exemple plus complexe](http://requirebin.com/?gist=6031068) (Vous êtes libre de changer le code pour voir ce qu'il se produit): 559 | [![requirebin](requirebin.png)](http://requirebin.com/embed?gist=6031068) 560 | 561 | ## Suivez le mouvement 562 | 563 | Comme tous les bons outils, node est particulièrement adapté à certains cas d'utilisation. Par exemple: Rails, le framework web populaire, est fantastique pour modeliser de la [logique métier complexe](http://en.wikipedia.org/wiki/Business_logic), c'est-à-dire utiliser le code pour représenter des objets métiers comme des comptes clients, des prêts, des itinéraires, ou encore des stocks. Tandis qu'il est techniquement possible de faire la même chose avec Node, nous rencontrerions quelques désagréments car Node n'a pas été conçu pour résoudre ce type de problématiques. Retenez que chaque outil se concentre sur des problèmes différents! Fort heureusement, ce guide vous aidera à comprendre les forces de node afin que vous sachiez intuitivement à quel moment y avoir recours. 564 | 565 | ### Quelles sont les limites du scope de node ? 566 | 567 | Node n'est fondamentalement conçu que pour gérer les I/O à travers le file system et les réseaux, et laisse les fonctionnalités plus fantaisistes aux modules tiers. Voici quelques exemples de choses qui dépassent le perimètre d'action de node: 568 | 569 | #### Frameworks Web 570 | 571 | Il existe des frameworks web basés sur node (framework signifiant un agglomérat de solutions qui cherchent à solutionner des problèmes au niveau comme de la logique métier), mais node n'est pas un framework à lui seul. Les frameworks web conçu par dessus node ne prennent pas les mêmes décisions concernant l'ajout de complexité, d'abstraction ou de compromis que node, et ont souvent d'autres priorités que les simples problématiques d'I/O. 572 | 573 | #### Syntaxe du langage 574 | 575 | Node utilise JavaScript et adopte donc sa syntaxe. Felix Geisendörfer présente une synthèse plutôt bonne du 'style node' [here](https://github.com/felixge/node-style-guide). 576 | 577 | #### Niveau d'abstraction du langage 578 | 579 | Quand cela est possible, node utilisera le moyen le plus simple possible d'accomplir quelque chose. Plus fantaisiste sera votre JavaScript plus vous apportez de complexité. Programmer est difficile, particulièrement en JavaScript où vous avez 1000 manières différentes de solutionner un même problème ! C'est pourquoi node essaye toujours d'utiliser la solution la plus simple universelle. Si vous tentez de résoudre un problème qui appelle une solution complexe, et que vous n'êtes pas satisfait des solutions en pure JS que node implémente, vous êtes libre de les résoudre à l'interieur de votre module en utilisant le niveau d'abstraction que vous souhaitez. 580 | 581 | Un exemple parfait pour cela est l'utilisation que node fait des callbacks. Plus tôt, node a fait l'experience d'une feature nommé 'promesses' qui ajoutaient un certain nombre de features pour rendre le code asynchrone plus linéaire. Les promesses furent retirées du coeur node pour plusieurs raisons : 582 | 583 | - Elles sont plus complexes que les callbacks 584 | - Elles peuvent être implémentées avec userland (distribué en module tiers via npm) 585 | 586 | Considérez une des choses les plus universelles et basique proposée par node: lire un fichier. Quand vous lisez un fichier vous voulez être au courant de l'apparition d'une erreur, comme lorsque votre disque dur meurt au milieu d'une lecture. Si node possédait des promesses, tout le monde devrait produire un code comme ceci : 587 | 588 | ```js 589 | fs.readFile('movie.mp4') 590 | .then(function(data) { 591 | // do stuff with data 592 | }) 593 | .error(function(error) { 594 | // handle error 595 | }) 596 | ``` 597 | 598 | Cela ajouterait de la complexité, et tout le monde ne souhaite pas cela. A la place de deux fonctions différentes, node n'appelle qu'une fonction de callback. Les règles sont les suivantes : 599 | 600 | - Quand il n'y a pas d'erreur, passez null en premier argument. 601 | - Quand il y a une erreur, passez la en premier argument. 602 | - Le reste des arguments peut être utilisé pour ce que vous désirez (en général les données ou réponses de vos flux d'I/O, puisque vous utiliserez généralement node à cet fin). 603 | 604 | En conséquence, voila le style node en callback: 605 | 606 | ```js 607 | fs.readFile('movie.mp4', function(err, data) { 608 | // handle error, do stuff with data 609 | }) 610 | ``` 611 | 612 | #### Threads/fibers/non-event-based concurrency solutions 613 | 614 | Note : Si vous ne savez pas ce que ces choses signifient, il sera probablement plus simple d'apprendre node, puisque désapprendre constitue tout autant de travail qu'apprendre. 615 | 616 | Node utilise des threads internes pour accelérer les choses, mais ne les expose par à l'utilisateur. Si vous êtes un utilisateur technique et que vous demandez pourquoi node est conçu ainsi, alors vous devriez absolument lire [the design of libuv](http://nikhilm.github.com/uvbook/), la couche I/O en C++ sur laquelle node est fondé. 617 | 618 | ## License 619 | 620 | ![CCBY](CCBY.png) 621 | 622 | Creative Commons Attribution License (do whatever, just attribute me) 623 | http://creativecommons.org/licenses/by/2.0/ 624 | 625 | L'icone de dont provient de [Noun Project](http://thenounproject.com/term/donate/285/) 626 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # The Art of Node 2 | ## An introduction to Node.js 3 | 4 | This document is intended for readers who know at least a little bit of a couple of things: 5 | 6 | - a scripting language like JavaScript, Ruby, Python, Perl, etc. If you aren't a programmer yet then it is probably easier to start by reading [JavaScript for Cats](http://jsforcats.com/). :cat2: 7 | - git and github. These are the open source collaboration tools that people in the node community use to share modules. You just need to know the basics. Here are three great intro tutorials: [1](https://github.com/jlord/git-it-electron#readme), [2](http://ericsteinborn.com/github-for-cats/#/), [3](http://opensourcerer.diy.org/) 8 | 9 | ## Table of contents 10 | 11 | - [Learn node interactively](#learn-node-interactively) 12 | - [Understanding node](#understanding-node) 13 | - [Core modules](#core-modules) 14 | - [Callbacks](#callbacks) 15 | - [Events](#events) 16 | - [Streams](#streams) 17 | - [Modules and npm](#modules) 18 | - [Client side development with npm](#client-side-development-with-npm) 19 | - [Going with the grain](#going-with-the-grain) 20 | 21 | ## Learn node interactively 22 | 23 | In addition to reading this guide it's super important to also bust out your favorite text editor and actually write some node code. I always find that when I just read some code in a book it never really clicks, but learning by writing code is a good way to grasp new programming concepts. 24 | 25 | ### NodeSchool.io 26 | 27 | [NodeSchool.io](http://nodeschool.io/) is a series of free + open source interactive workshops that teach you the principles of Node.js and beyond. 28 | 29 | [Learn You The Node.js](https://github.com/workshopper/learnyounode#learn-you-the-nodejs-for-much-win) is the introductory NodeSchool.io workshop. It's a set of programming problems that introduce you to common node patterns. It comes packaged as a command line program. 30 | 31 | [![learnyounode](https://github.com/rvagg/learnyounode/raw/master/learnyounode.png)](https://github.com/rvagg/learnyounode#learn-you-the-nodejs-for-much-win) 32 | 33 | You can install it with npm: 34 | 35 | ``` 36 | # install 37 | npm install learnyounode -g 38 | 39 | # start the menu 40 | learnyounode 41 | ``` 42 | 43 | ## Understanding node 44 | 45 | Node.js is an open source project designed to help you write JavaScript programs that talk to networks, file systems or other I/O (input/output, reading/writing) sources. That's it! It is just a simple and stable I/O platform that you are encouraged to build modules on top of. 46 | 47 | What are some examples of I/O? Here is a diagram of an application that I made with node that shows many I/O sources: 48 | 49 | ![server diagram](server-diagram.png) 50 | 51 | If you don't understand all of the different things in the diagram it is completely okay. The point is to show that a single node process (the hexagon in the middle) can act as the broker between all of the different I/O endpoints (orange and purple represent I/O). 52 | 53 | Usually building these kinds of systems is either: 54 | 55 | - difficult to code but yields super fast results (like writing your web servers from scratch in C) 56 | - easy to code but not very speedy/robust (like when someone tries to upload a 5GB file and your server crashes) 57 | 58 | Node's goal is to strike a balance between these two: relatively easy to understand and use and fast enough for most use cases. 59 | 60 | Node isn't either of the following: 61 | 62 | - A web framework (like Rails or Django, though it can be used to make such things) 63 | - A programming language (it uses JavaScript but node isn't its own language) 64 | 65 | Instead, node is somewhere in the middle. It is: 66 | 67 | - Designed to be simple and therefore relatively easy to understand and use 68 | - Useful for I/O based programs that need to be fast and/or handle lots of connections 69 | 70 | At a lower level, node can be described as a tool for writing two major types of programs: 71 | 72 | - Network programs using the protocols of the web: HTTP, TCP, UDP, DNS and SSL 73 | - Programs that read and write data to the filesystem or local processes/memory 74 | 75 | What is an "I/O based program"? Here are some common I/O sources: 76 | 77 | - Databases (e.g. MySQL, PostgreSQL, MongoDB, Redis, CouchDB) 78 | - APIs (e.g. Twitter, Facebook, Apple Push Notifications) 79 | - HTTP/WebSocket connections (from users of a web app) 80 | - Files (image resizer, video editor, internet radio) 81 | 82 | Node does I/O in a way that is [asynchronous](https://en.wikipedia.org/wiki/Asynchronous_I/O) which lets it handle lots of different things simultaneously. For example, if you go down to a fast food joint and order a cheeseburger they will immediately take your order and then make you wait around until the cheeseburger is ready. In the meantime they can take other orders and start cooking cheeseburgers for other people. Imagine if you had to wait at the register for your cheeseburger, blocking all other people in line from ordering while they cooked your burger! This is called **blocking I/O** because all I/O (cooking cheeseburgers) happens one at a time. Node, on the other hand, is **non-blocking**, which means it can cook many cheeseburgers at once. 83 | 84 | Here are some fun things made easy with node thanks to its non-blocking nature: 85 | 86 | - Control [flying quadcopters](http://www.nodecopter.com/) 87 | - Write IRC chat bots 88 | - Create [walking biped robots](https://www.youtube.com/watch?v=jf-cEB3U2UQ) 89 | 90 | ## Core modules 91 | 92 | Firstly I would recommend that you get node installed on your computer. The easiest way is to visit [nodejs.org](http://nodejs.org) and click `Install`. 93 | 94 | Node has a small core group of modules (commonly referred to as 'node core') that are presented as the public API that you are intended to write programs with. For working with file systems there is the `fs` module and for networks there are modules like `net` (TCP), `http`, `dgram` (UDP). 95 | 96 | In addition to `fs` and network modules there are a number of other base modules in node core. There is a module for asynchronously resolving DNS queries called `dns`, a module for getting OS specific information like the tmpdir location called `os`, a module for allocating binary chunks of memory called `buffer`, some modules for parsing urls and paths (`url`, `querystring`, `path`), etc. Most if not all of the modules in node core are there to support node's main use case: writing fast programs that talk to file systems or networks. 97 | 98 | Node handles I/O with: callbacks, events, streams and modules. If you learn how these four things work then you will be able to go into any module in node core and have a basic understanding about how to interface with it. 99 | 100 | ## Callbacks 101 | 102 | This is the most important topic to understand if you want to understand how to use node. Nearly everything in node uses callbacks. They weren't invented by node, they are just part of the JavaScript language. 103 | 104 | Callbacks are functions that are executed asynchronously, or at a later time. Instead of the code reading top to bottom procedurally, async programs may execute different functions at different times based on the order and speed that earlier functions like http requests or file system reads happen. 105 | 106 | The difference can be confusing since determining if a function is asynchronous or not depends a lot on context. Here is a simple synchronous example, meaning you can read the code top to bottom just like a book: 107 | 108 | ```js 109 | var myNumber = 1 110 | function addOne() { myNumber++ } // define the function 111 | addOne() // run the function 112 | console.log(myNumber) // logs out 2 113 | ``` 114 | 115 | The code here defines a function and then on the next line calls that function, without waiting for anything. When the function is called it immediately adds 1 to the number, so we can expect that after we call the function the number should be 2. This is the expectation of synchronous code - it sequentially runs top to bottom. 116 | 117 | Node, however, uses mostly asynchronous code. Let's use node to read our number from a file called `number.txt`: 118 | 119 | ```js 120 | var fs = require('fs') // require is a special function provided by node 121 | var myNumber = undefined // we don't know what the number is yet since it is stored in a file 122 | 123 | function addOne() { 124 | fs.readFile('number.txt', function doneReading(err, fileContents) { 125 | myNumber = parseInt(fileContents) 126 | myNumber++ 127 | }) 128 | } 129 | 130 | addOne() 131 | 132 | console.log(myNumber) // logs out undefined -- this line gets run before readFile is done 133 | ``` 134 | 135 | Why do we get `undefined` when we log out the number this time? In this code we use the `fs.readFile` method, which happens to be an asynchronous method. Usually things that have to talk to hard drives or networks will be asynchronous. If they just have to access things in memory or do some work on the CPU they will be synchronous. The reason for this is that I/O is reallyyy reallyyy sloowwww. A ballpark figure would be that talking to a hard drive is about 100,000 times slower than talking to memory (e.g. RAM). 136 | 137 | When we run this program all of the functions are immediately defined, but they don't all execute immediately. This is a fundamental thing to understand about async programming. When `addOne` is called it kicks off a `readFile` and then moves on to the next thing that is ready to execute. If there is nothing to execute node will either wait for pending fs/network operations to finish or it will stop running and exit to the command line. 138 | 139 | When `readFile` is done reading the file (this may take anywhere from milliseconds to seconds to minutes depending on how fast the hard drive is) it will run the `doneReading` function and give it an error (if there was an error) and the file contents. 140 | 141 | The reason we got `undefined` above is that nowhere in our code exists logic that tells the `console.log` statement to wait until the `readFile` statement finishes before it prints out the number. 142 | 143 | If you have some code that you want to be able to execute over and over again, or at a later time, the first step is to put that code inside a function. Then you can call the function whenever you want to run your code. It helps to give your functions descriptive names. 144 | 145 | Callbacks are just functions that get executed at some later time. The key to understanding callbacks is to realize that they are used when you don't know **when** some async operation will complete, but you do know **where** the operation will complete — the last line of the async function! The top-to-bottom order that you declare callbacks does not necessarily matter, only the logical/hierarchical nesting of them. First you split your code up into functions, and then use callbacks to declare if one function depends on another function finishing. 146 | 147 | The `fs.readFile` method is provided by node, is asynchronous, and happens to take a long time to finish. Consider what it does: it has to go to the operating system, which in turn has to go to the file system, which lives on a hard drive that may or may not be spinning at thousands of revolutions per minute. Then it has to use a magnetic head to read data and send it back up through the layers back into your javascript program. You give `readFile` a function (known as a callback) that it will call after it has retrieved the data from the file system. It puts the data it retrieved into a javascript variable and calls your function (callback) with that variable. In this case the variable is called `fileContents` because it contains the contents of the file that was read. 148 | 149 | Think of the restaurant example at the beginning of this tutorial. At many restaurants you get a number to put on your table while you wait for your food. These are a lot like callbacks. They tell the server what to do after your cheeseburger is done. 150 | 151 | Let's put our `console.log` statement into a function and pass it in as a callback: 152 | 153 | ```js 154 | var fs = require('fs') 155 | var myNumber = undefined 156 | 157 | function addOne(callback) { 158 | fs.readFile('number.txt', function doneReading(err, fileContents) { 159 | myNumber = parseInt(fileContents) 160 | myNumber++ 161 | callback() 162 | }) 163 | } 164 | 165 | function logMyNumber() { 166 | console.log(myNumber) 167 | } 168 | 169 | addOne(logMyNumber) 170 | ``` 171 | 172 | Now the `logMyNumber` function can get passed in as an argument that will become the `callback` variable inside the `addOne` function. After `readFile` is done the `callback` variable will be invoked (`callback()`). Only functions can be invoked, so if you pass in anything other than a function it will cause an error. 173 | 174 | When a function gets invoked in javascript the code inside that function will immediately get executed. In this case our log statement will execute since `callback` is actually `logMyNumber`. Remember, just because you *define* a function it doesn't mean it will execute. You have to *invoke* a function for that to happen. 175 | 176 | To break down this example even more, here is a timeline of events that happen when we run this program: 177 | 178 | - 1: The code is parsed, which means if there are any syntax errors they would make the program break. During this initial phase, `fs` and `myNumber` are declared as variables while `addOne` and `logMyNumber` are declared as functions. Note that these are just declarations. Neither function has been called nor invoked yet. 179 | - 2: When the last line of our program gets executed `addOne` is invoked with the `logMyNumber` function passed as its `callback` argument. Invoking `addOne` will first run the asynchronous `fs.readFile` function. This part of the program takes a while to finish. 180 | - 3: With nothing to do, node idles for a bit as it waits for `readFile` to finish. If there was anything else to do during this time, node would be available for work. 181 | - 4: As soon as `readFile` finishes it executes its callback, `doneReading`, which parses `fileContents` for an integer called `myNumber`, increments `myNumber` and then immediately invokes the function that `addOne` passed in (its callback), `logMyNumber`. 182 | 183 | Perhaps the most confusing part of programming with callbacks is how functions are just objects that can be stored in variables and passed around with different names. Giving simple and descriptive names to your variables is important in making your code readable by others. Generally speaking in node programs when you see a variable like `callback` or `cb` you can assume it is a function. 184 | 185 | You may have heard the terms 'evented programming' or 'event loop'. They refer to the way that `readFile` is implemented. Node first dispatches the `readFile` operation and then waits for `readFile` to send it an event that it has completed. While it is waiting node can go check on other things. Inside node there is a list of things that are dispatched but haven't reported back yet, so node loops over the list again and again checking to see if they are finished. After they finished they get 'processed', e.g. any callbacks that depended on them finishing will get invoked. 186 | 187 | Here is a pseudocode version of the above example: 188 | 189 | ```js 190 | function addOne(thenRunThisFunction) { 191 | waitAMinuteAsync(function waitedAMinute() { 192 | thenRunThisFunction() 193 | }) 194 | } 195 | 196 | addOne(function thisGetsRunAfterAddOneFinishes() {}) 197 | ``` 198 | 199 | Imagine you had 3 async functions `a`, `b` and `c`. Each one takes 1 minute to run and after it finishes it calls a callback (that gets passed in the first argument). If you wanted to tell node 'start running a, then run b after a finishes, and then run c after b finishes' it would look like this: 200 | 201 | ```js 202 | a(function() { 203 | b(function() { 204 | c() 205 | }) 206 | }) 207 | ``` 208 | 209 | When this code gets executed, `a` will immediately start running, then a minute later it will finish and call `b`, then a minute later it will finish and call `c` and finally 3 minutes later node will stop running since there would be nothing more to do. There are definitely more elegant ways to write the above example, but the point is that if you have code that has to wait for some other async code to finish then you express that dependency by putting your code in functions that get passed around as callbacks. 210 | 211 | The design of node requires you to think non-linearly. Consider this list of operations: 212 | 213 | ``` 214 | read a file 215 | process that file 216 | ``` 217 | 218 | If you were to turn this into pseudocode you would end up with this: 219 | 220 | ``` 221 | var file = readFile() 222 | processFile(file) 223 | ``` 224 | 225 | This kind of linear (step-by-step, in order) code isn't the way that node works. If this code were to get executed then `readFile` and `processFile` would both get executed at the same exact time. This doesn't make sense since `readFile` will take a while to complete. Instead you need to express that `processFile` depends on `readFile` finishing. This is exactly what callbacks are for! And because of the way that JavaScript works you can write this dependency many different ways: 226 | 227 | ```js 228 | var fs = require('fs') 229 | fs.readFile('movie.mp4', finishedReading) 230 | 231 | function finishedReading(error, movieData) { 232 | if (error) return console.error(error) 233 | // do something with the movieData 234 | } 235 | ``` 236 | 237 | But you could also structure your code like this and it would still work: 238 | 239 | ```js 240 | var fs = require('fs') 241 | 242 | function finishedReading(error, movieData) { 243 | if (error) return console.error(error) 244 | // do something with the movieData 245 | } 246 | 247 | fs.readFile('movie.mp4', finishedReading) 248 | ``` 249 | 250 | Or even like this: 251 | 252 | ```js 253 | var fs = require('fs') 254 | 255 | fs.readFile('movie.mp4', function finishedReading(error, movieData) { 256 | if (error) return console.error(error) 257 | // do something with the movieData 258 | }) 259 | ``` 260 | 261 | ## Events 262 | 263 | In node if you require the [events](https://nodejs.org/api/events.html) module you can use the so-called 'event emitter' that node itself uses for all of its APIs that emit things. 264 | 265 | Events are a common pattern in programming, known more widely as the ['observer pattern'](https://en.wikipedia.org/wiki/Observer_pattern) or 'pub/sub' (publish/subscribe). Whereas callbacks are a one-to-one relationship between the thing waiting for the callback and the thing calling the callback, events are the same exact pattern except with a many-to-many API. 266 | 267 | The easiest way to think about events is that they let you subscribe to things. You can say 'when X do Y', whereas with plain callbacks it is 'do X then Y'. 268 | 269 | Here are few common use cases for using events instead of plain callbacks: 270 | 271 | - Chat room where you want to broadcast messages to many listeners 272 | - Game server that needs to know when new players connect, disconnect, move, shoot and jump 273 | - Game engine where you want to let game developers subscribe to events like `.on('jump', function() {})` 274 | - A low level web server that wants to expose an API to easily hook into events that happen like `.on('incomingRequest')` or `.on('serverError')` 275 | 276 | If we were trying to write a module that connects to a chat server using only callbacks it would look like this: 277 | 278 | ```js 279 | var chatClient = require('my-chat-client') 280 | 281 | function onConnect() { 282 | // have the UI show we are connected 283 | } 284 | 285 | function onConnectionError(error) { 286 | // show error to the user 287 | } 288 | 289 | function onDisconnect() { 290 | // tell user that they have been disconnected 291 | } 292 | 293 | function onMessage(message) { 294 | // show the chat room message in the UI 295 | } 296 | 297 | chatClient.connect( 298 | 'http://mychatserver.com', 299 | onConnect, 300 | onConnectionError, 301 | onDisconnect, 302 | onMessage 303 | ) 304 | ``` 305 | 306 | As you can see this is really cumbersome because of all of the functions that you have to pass in a specific order to the `.connect` function. Writing this with events would look like this: 307 | 308 | ```js 309 | var chatClient = require('my-chat-client').connect() 310 | 311 | chatClient.on('connect', function() { 312 | // have the UI show we are connected 313 | }) 314 | 315 | chatClient.on('connectionError', function() { 316 | // show error to the user 317 | }) 318 | 319 | chatClient.on('disconnect', function() { 320 | // tell user that they have been disconnected 321 | }) 322 | 323 | chatClient.on('message', function() { 324 | // show the chat room message in the UI 325 | }) 326 | ``` 327 | 328 | This approach is similar to the pure-callback approach but introduces the `.on` method, which subscribes a callback to an event. This means you can choose which events you want to subscribe to from the `chatClient`. You can also subscribe to the same event multiple times with different callbacks: 329 | 330 | ```js 331 | var chatClient = require('my-chat-client').connect() 332 | chatClient.on('message', logMessage) 333 | chatClient.on('message', storeMessage) 334 | 335 | function logMessage(message) { 336 | console.log(message) 337 | } 338 | 339 | function storeMessage(message) { 340 | myDatabase.save(message) 341 | } 342 | ``` 343 | 344 | ## Streams 345 | 346 | Early on in the node project the file system and network APIs had their own separate patterns for dealing with streaming I/O. For example, files in a file system have things called 'file descriptors' so the `fs` module had to have extra logic to keep track of these things whereas the network modules didn't have such a concept. Despite minor differences in semantics like these, at a fundamental level both groups of code were duplicating a lot of functionality when it came to reading data in and out. The team working on node realized that it would be confusing to have to learn two sets of semantics to essentially do the same thing so they made a new API called the `Stream` and made all the network and file system code use it. 347 | 348 | The whole point of node is to make it easy to deal with file systems and networks so it made sense to have one pattern that was used everywhere. The good news is that most of the patterns like these (there are only a few anyway) have been figured out at this point and it is very unlikely that node will change that much in the future. 349 | 350 | There are already two great resources that you can use to learn about node streams. One is the stream-adventure (see the Learn Node Interactively section) and the other is a reference called the Stream Handbook. 351 | 352 | ### Stream Handbook 353 | 354 | [stream-handbook](https://github.com/substack/stream-handbook#introduction) is a guide, similar to this one, that contains a reference for everything you could possibly need to know about streams. 355 | 356 | [![stream-handbook](stream-handbook.png)](https://github.com/substack/stream-handbook) 357 | 358 | ## Modules 359 | 360 | Node core is made up of about two dozen modules, some lower level ones like `events` and `stream` some higher level ones like `http` and `crypto`. 361 | 362 | This design is intentional. Node core is supposed to be small, and the modules in core should be focused on providing tools for working with common I/O protocols and formats in a way that is cross-platform. 363 | 364 | For everything else there is [npm](https://www.npmjs.com/). Anyone can create a new node module that adds some functionality and publish it to npm. As of the time of this writing there are 34,000 modules on npm. 365 | 366 | ### How to find a module 367 | 368 | Imagine you are trying to convert PDF files into TXT files. The best place to start is by doing `npm search pdf`: 369 | 370 | ![pdfsearch](npm-search.png) 371 | 372 | There are a ton of results! npm is quite popular and you will usually be able to find multiple potential solutions. If you go through each module and whittle down the results into a more narrow set (filtering out things like PDF generation modules) you'll end up with these: 373 | 374 | - [hummus](https://github.com/galkahana/HummusJS/wiki/Features) - c++ pdf manipulator 375 | - [mimeograph](https://github.com/steelThread/mimeograph) - api on a conglomeration of tools (poppler, tesseract, imagemagick etc) 376 | - [pdftotextjs](https://www.npmjs.com/package/pdftotextjs) - wrapper around [pdftotext](https://en.wikipedia.org/wiki/Pdftotext) 377 | - [pdf-text-extract](https://www.npmjs.com/package/pdf-text-extract) - another wrapper around pdftotext 378 | - [pdf-extract](https://www.npmjs.com/package/pdf-extract) - wrapper around pdftotext, pdftk, tesseract, ghostscript 379 | - [pdfutils](https://www.npmjs.com/package/pdfutils) - poppler wrapper 380 | - [scissors](https://www.npmjs.com/package/scissors) - pdftk, ghostscript wrapper w/ high level api 381 | - [textract](https://www.npmjs.com/package/textract) - pdftotext wrapper 382 | - [pdfiijs](https://github.com/fagbokforlaget/pdfiijs) - pdf to inverted index using textiijs and poppler 383 | - [pdf2json](https://github.com/modesty/pdf2json/blob/master/readme.md) - pure js pdf to json 384 | 385 | A lot of the modules have overlapping functionality but present alternate APIs and most of them require external dependencies (like `apt-get install poppler`). 386 | 387 | Here are some different ways to interpret the modules: 388 | 389 | - `pdf2json` is the only one that is written in pure JavaScript, which means it is the easiest to install, especially on low power devices like the raspberry pi or on Windows where native code might not be cross platform. 390 | - modules like `mimeograph`, `hummus` and `pdf-extract` each combine multiple lower level modules to expose a high level API 391 | - a lot of modules seem to sit on top of the `pdftotext`/`poppler` unix command line tools 392 | 393 | Lets compare the differences between `pdftotextjs` and `pdf-text-extract`, both of which are are wrappers around the `pdftotext` utility. 394 | 395 | ![pdf-modules](pdf-modules.png) 396 | 397 | Both of these: 398 | 399 | - were updated relatively recently 400 | - have github repositories linked (this is very important!) 401 | - have READMEs 402 | - have at least some number of people installing them every week 403 | - are liberally licensed (anyone can use them) 404 | 405 | Just looking at the `package.json` + module statistics it's hard to get a feeling about which one might be the right choice. Let's compare the READMEs: 406 | 407 | ![pdf-readmes](pdf-readmes.png) 408 | 409 | Both have simple descriptions, CI badges, installation instructions, clear examples and instructions for running the tests. Great! But which one do we use? Let's compare the code: 410 | 411 | ![pdf-code](pdf-code.png) 412 | 413 | `pdftotextjs` is around 110 lines of code, and `pdf-text-extract` is around 40, but both essentially boil down to this line: 414 | 415 | ``` 416 | var child = shell.exec('pdftotext ' + self.options.additional.join(' ')); 417 | ``` 418 | 419 | Does this make one any better than the other? Hard to say! It's important to actually *read* the code and make your own conclusions. If you find a module you like, use `npm star modulename` to give npm feedback about modules that you had a positive experience with. 420 | 421 | ### Modular development workflow 422 | 423 | npm is different from most package managers in that it installs modules into a folder inside of other existing modules. The previous sentence might not make sense right now but it is the key to npm's success. 424 | 425 | Many package managers install things globally. For instance, if you `apt-get install couchdb` on Debian Linux it will try to install the latest stable version of CouchDB. If you are trying to install CouchDB as a dependency of some other piece of software and that software needs an older version of CouchDB, you have to uninstall the newer version of CouchDB and then install the older version. You can't have two versions of CouchDB installed because Debian only knows how to install things into one place. 426 | 427 | It's not just Debian that does this. Most programming language package managers work this way too. To address the global dependencies problem described above there have been virtual environment developed like [virtualenv](http://python-guide.readthedocs.org/en/latest/dev/virtualenvs/) for Python or [bundler](http://bundler.io/) for Ruby. These just split your environment up in to many virtual environments, one for each project, but inside each environment dependencies are still globally installed. Virtual environments don't always solve the problem, sometimes they just multiply it by adding additional layers of complexity. 428 | 429 | With npm installing global modules is an anti-pattern. Just like how you shouldn't use global variables in your JavaScript programs you also shouldn't install global modules (unless you need a module with an executable binary to show up in your global `PATH`, but you don't always need to do this -- more on this later). 430 | 431 | #### How `require` works 432 | 433 | When you call `require('some_module')` in node here is what happens: 434 | 435 | 1. if a file called `some_module.js` exists in the current folder node will load that, otherwise: 436 | 2. node looks in the current folder for a `node_modules` folder with a `some_module` folder in it 437 | 3. if it doesn't find it, it will go up one folder and repeat step 2 438 | 439 | This cycle repeats until node reaches the root folder of the filesystem, at which point it will then check any global module folders (e.g. `/usr/local/node_modules` on Mac OS) and if it still doesn't find `some_module` it will throw an exception. 440 | 441 | Here's a visual example: 442 | 443 | ![mod-diagram-01](mod-diagram-01.png) 444 | 445 | When the current working directory is `subsubfolder` and `require('foo')` is called, node will look for the folder called `subsubfolder/node_modules`. In this case it won't find it -- the folder there is mistakenly called `my_modules`. Then node will go up one folder and try again, meaning it then looks for `subfolder_B/node_modules`, which also doesn't exist. Third try is a charm, though, as `folder/node_modules` does exist *and* has a folder called `foo` inside of it. If `foo` wasn't in there node would continue its search up the directory tree. 446 | 447 | Note that if called from `subfolder_B` node will never find `subfolder_A/node_modules`, it can only see `folder/node_modules` on its way up the tree. 448 | 449 | One of the benefits of npm's approach is that modules can install their dependent modules at specific known working versions. In this case the module `foo` is quite popular - there are three copies of it, each one inside its parent module folder. The reasoning for this could be that each parent module needed a different version of `foo`, e.g. 'folder' needs `foo@0.0.1`, `subfolder_A` needs `foo@0.2.1` etc. 450 | 451 | Here's what happens when we fix the folder naming error by changing `my_modules` to the correct name `node_modules`: 452 | 453 | ![mod-diagram-02](mod-diagram-02.png) 454 | 455 | To test out which module actually gets loaded by node, you can use the `require.resolve('some_module')` command, which will show you the path to the module that node finds as a result of the tree climbing process. `require.resolve` can be useful when double-checking that the module that you *think* is getting loaded is *actually* getting loaded -- sometimes there is another version of the same module closer to your current working directory than the one you intend to load. 456 | 457 | ### How to write a module 458 | 459 | Now that you know how to find modules and require them you can start writing your own modules. 460 | 461 | #### The simplest possible module 462 | 463 | Node modules are radically lightweight. Here is one of the simplest possible node modules: 464 | 465 | `package.json`: 466 | ```js 467 | { 468 | "name": "number-one", 469 | "version": "1.0.0" 470 | } 471 | ``` 472 | 473 | `index.js`: 474 | ```js 475 | module.exports = 1 476 | ``` 477 | 478 | By default node tries to load `module/index.js` when you `require('module')`, any other file name won't work unless you set the `main` field of `package.json` to point to it. 479 | 480 | Put both of those files in a folder called `number-one` (the `name` in `package.json` must match the folder name) and you'll have a working node module. 481 | 482 | Calling the function `require('number-one')` returns the value of whatever `module.exports` is set to inside the module: 483 | 484 | ![simple-module](simple-module.png) 485 | 486 | An even quicker way to create a module is to run these commands: 487 | 488 | ```sh 489 | mkdir my_module 490 | cd my_module 491 | git init 492 | git remote add git@github.com:yourusername/my_module.git 493 | npm init 494 | ``` 495 | 496 | Running `npm init` will create a valid `package.json` for you and if you run it in an existing `git` repo it will set the `repositories` field inside `package.json` automatically as well! 497 | 498 | #### Adding dependencies 499 | 500 | A module can list any other modules from npm or GitHub in the `dependencies` field of `package.json`. To install the `request` module as a new dependency and automatically add it to `package.json` run this from your module root directory: 501 | 502 | ```sh 503 | npm install --save request 504 | ``` 505 | 506 | This installs a copy of `request` into the closest `node_modules` folder and makes our `package.json` look something like this: 507 | 508 | ``` 509 | { 510 | "id": "number-one", 511 | "version": "1.0.0", 512 | "dependencies": { 513 | "request": "~2.22.0" 514 | } 515 | } 516 | ``` 517 | 518 | By default `npm install` will grab the latest published version of a module. 519 | 520 | ## Client side development with npm 521 | 522 | A common misconception about npm is that since it has 'Node' in the name that it must only be used for server side JS modules. This is completely untrue! npm actually stands for Node Packaged Modules, e.g. modules that Node packages together for you. The modules themselves can be whatever you want -- they are just a folder of files wrapped up in a .tar.gz, and a file called `package.json` that declares the module version and a list of all modules that are dependencies of the module (as well as their version numbers so the working versions get installed automatically). It's turtles all the way down - module dependencies are just modules, and those modules can have dependencies etc. etc. etc. 523 | 524 | [browserify](http://browserify.org/) is a utility written in Node that tries to convert any node module into code that can be run in browsers. Not all modules work (browsers can't do things like host an HTTP server), but a lot of modules on NPM *will* work. 525 | 526 | To try out npm in the browser you can use [RequireBin](http://requirebin.com/), an app I made that takes advantage of [Browserify-CDN](https://github.com/jfhbrook/wzrd.in), which internally uses browserify but returns the output through HTTP (instead of the command line -- which is how browserify is usually used). 527 | 528 | Try putting this code into RequireBin and then hit the preview button: 529 | 530 | ```js 531 | var reverse = require('ascii-art-reverse') 532 | 533 | // makes a visible HTML console 534 | require('console-log').show(true) 535 | 536 | var coolbear = 537 | " ('-^-/') \n" + 538 | " `o__o' ] \n" + 539 | " (_Y_) _/ \n" + 540 | " _..`--'-.`, \n" + 541 | " (__)_,--(__) \n" + 542 | " 7: ; 1 \n" + 543 | " _/,`-.-' : \n" + 544 | " (_,)-~~(_,) \n" 545 | 546 | setInterval(function() { console.log(coolbear) }, 1000) 547 | 548 | setTimeout(function() { 549 | setInterval(function() { console.log(reverse(coolbear)) }, 1000) 550 | }, 500) 551 | ``` 552 | 553 | Or check out a [more complicated example](http://requirebin.com/?gist=679b58d4237eaca37173) (feel free to change the code and see what happens): 554 | 555 | [![requirebin](requirebin.png)](http://requirebin.com/embed?gist=679b58d4237eaca37173) 556 | 557 | ## Going with the grain 558 | 559 | Like any good tool, node is best suited for a certain set of use cases. For example: Rails, the popular web framework, is great for modeling complex [business logic](https://en.wikipedia.org/wiki/Business_logic), e.g. using code to represent real life business objects like accounts, loan, itineraries, and inventories. While it is technically possible to do the same type of thing using node, there would be definite drawbacks since node is designed for solving I/O problems and it doesn't know much about 'business logic'. Each tool focuses on different problems. Hopefully this guide will help you gain an intuitive understanding of the strengths of node so that you know when it can be useful to you. 560 | 561 | ### What is outside of node's scope? 562 | 563 | Fundamentally node is just a tool used for managing I/O across file systems and networks, and it leaves other more fancy functionality up to third party modules. Here are some things that are outside the scope of node: 564 | 565 | #### Web frameworks 566 | 567 | There are a number of web frameworks built on top of node (framework meaning a bundle of solutions that attempts to address some high level problem like modeling business logic), but node is not a web framework. Web frameworks that are written using node don't always make the same kind of decisions about adding complexity, abstractions and tradeoffs that node does and may have other priorities. 568 | 569 | #### Language syntax 570 | 571 | Node uses JavaScript and doesn't change anything about it. Felix Geisendörfer has a pretty good write-up of the 'node style' [here](https://github.com/felixge/node-style-guide). 572 | 573 | #### Language abstraction 574 | 575 | When possible node will use the simplest possible way of accomplishing something. The 'fancier' you make your JavaScript the more complexity and tradeoffs you introduce. Programming is hard, especially in JS where there are 1000 solutions to every problem! It is for this reason that node tries to always pick the simplest, most universal option. If you are solving a problem that calls for a complex solution and you are unsatisfied with the 'vanilla JS solutions' that node implements, you are free to solve it inside your app or module using whichever abstractions you prefer. 576 | 577 | A great example of this is node's use of callbacks. Early on node experimented with a feature called 'promises' that added a number of features to make async code appear more linear. It was taken out of node core for a few reasons: 578 | 579 | - they are more complex than callbacks 580 | - they can be implemented in userland (distributed on npm as third party modules) 581 | 582 | Consider one of the most universal and basic things that node does: reading a file. When you read a file you want to know when errors happen, like when your hard drive dies in the middle of your read. If node had promises everyone would have to branch their code like this: 583 | 584 | ```js 585 | fs.readFile('movie.mp4') 586 | .then(function(data) { 587 | // do stuff with data 588 | }) 589 | .error(function(error) { 590 | // handle error 591 | }) 592 | ``` 593 | 594 | This adds complexity, and not everyone wants that. Instead of two separate functions node just uses a single callback function. Here are the rules: 595 | 596 | - When there is no error pass null as the first argument 597 | - When there is an error, pass it as the first argument 598 | - The rest of the arguments can be used for anything (usually data or responses since most stuff in node is reading or writing things) 599 | 600 | Hence, the node callback style: 601 | 602 | ```js 603 | fs.readFile('movie.mp4', function(err, data) { 604 | // handle error, do stuff with data 605 | }) 606 | ``` 607 | 608 | #### Threads/fibers/non-event-based concurrency solutions 609 | 610 | Note: If you don't know what these things mean then you will likely have an easier time learning node, since unlearning things is just as much work as learning things. 611 | 612 | Node uses threads internally to make things fast but doesn't expose them to the user. If you are a technical user wondering why node is designed this way then you should 100% read about [the design of libuv](http://nikhilm.github.io/uvbook/), the C++ I/O layer that node is built on top of. 613 | 614 | ## License 615 | 616 | ![CCBY](CCBY.png) 617 | 618 | Creative Commons Attribution License (do whatever, just attribute me) 619 | http://creativecommons.org/licenses/by/2.0/ 620 | 621 | Donate icon is from the [Noun Project](https://thenounproject.com/term/donate/285/) 622 | -------------------------------------------------------------------------------- /readme.pt-br.md: -------------------------------------------------------------------------------- 1 | # A arte do Node 2 | 3 | ## Uma introdução ao Node.js 4 | 5 | Este documento é destinado à leitores que sabem no mínimo algumas das coisas abaixo: 6 | 7 | - Uma linguagem de script como JavaScript, Ruby, Python, Perl, etc. Se você ainda não é um programador então é, provavelmente, mais fácil começar a ler [JavaScript for Cats](http://jsforcats.com/). :cat2: 8 | - Git e Github. Estas são ferramentas de colaboração de código aberto que pessoas da comunidade Node usam para compartilhar módulos. Você só precisa saber o básico. Aqui estão três ótimos tutoriais de introdução. [1](https://github.com/jlord/git-it-electron#readme), [2](http://zachbruggeman.me/github-for-cats/), [3](http://opensourcerer.diy.org/) (em inglês). 9 | 10 | Este pequeno livro é um trabalho em progresso. Se você gostar deste livro considere **fazer uma doação** via [gittip](https://www.gittip.com/maxogden/) para que eu possa escrever muito mais. 11 | 12 | [![donate](donate.png)](https://www.gittip.com/maxogden/) 13 | 14 | ## Tabela de Conteúdo 15 | 16 | - [Aprenda Node de forma interativa](#aprenda-node-de-forma-interativa) 17 | - [Entendendo Node](#entendendo-node) 18 | - [Módulos do núcleo](#módulos-do-núcleo) 19 | - [Callbacks](#callbacks) 20 | - [Eventos](#eventos) 21 | - [Streams](#streams) 22 | - [Módulos e NPM](#módulos-e-npm) 23 | - [Desenvolvimento no lado do cliente com NPM](#desenvolvimento-no-lado-do-cliente-com-npm) 24 | - [Evoluindo de forma correta](#evoluindo-de-forma-correta) 25 | 26 | ## Aprenda Node de forma interativa 27 | 28 | Como complemento para a leitura deste guia, é super importante que você também inicie o seu editor de texto e comece desde já a escrever alguns códigos em Node. Eu sempre acho que quando acabo de ler algum código em um livro ele nunca realmente é executado, mas aprender a escrever códigos é uma boa maneira para compreender os novos conceitos de programação. 29 | 30 | Aqui estão dois grandes tutoriais que você pode instalar no seu computador e permitirá a você aprender o Node de uma forma mais interativa: 31 | 32 | ### Learn You The Node.js 33 | 34 | [Learn You The Node.js](https://github.com/rvagg/learnyounode#learn-you-the-nodejs-for-much-win) é um conjunto de problemas de programação que vai apresentá-lo aos padrões do Node mais comuns. Ele vem como um conjunto de tutoriais interativos de linha de comando. 35 | 36 | [![learnyounode](https://github.com/rvagg/learnyounode/raw/master/learnyounode.png)](https://github.com/rvagg/learnyounode#learn-you-the-nodejs-for-much-win) 37 | 38 | Você pode instalar com o npm: 39 | 40 | ``` 41 | # instalação 42 | npm install learnyounode -g 43 | 44 | # inicialização 45 | learnyounode 46 | ``` 47 | 48 | ### Stream Adventure 49 | 50 | Depois que você finalizar o `learnyounode`, prossiga para o [stream-adventure](https://github.com/substack/stream-adventure) para um conjunto de exercícios a fim de se aprofundar mais ainda no Node. 51 | 52 | ``` 53 | # instalação 54 | npm install stream-adventure -g 55 | 56 | # inicialização 57 | stream-adventure 58 | ``` 59 | 60 | [![stream-adventure](stream-adventure.png)](https://github.com/substack/stream-adventure) 61 | 62 | ## Entendendo Node 63 | 64 | Node.js é um projeto de código aberto feito para te ajudar a escrever programas JavaScript que se comunicam com a rede, sistemas de arquivo ou outros códigos I/O (Entrada/Saida, Leitura/Escrita). Apenas isso! Node é apenas uma simples e estável plataforma I/O que encoraja a construção de módulos sobre ela mesma. 65 | 66 | Quais são os exemplos de I/O? Aqui está um diagrama de uma aplicação que foi feita com Node que mostra algumas fontes I/O: 67 | 68 | ![server diagram](server-diagram.png) 69 | 70 | Se você não entende todo os elementos do diagrama está tudo bem. O ponto é mostrar que um simples processo em Node (o hexágono no meio) pode atuar como um agente entre todos os pontos finais de I/O (laranja e roxo representam I/O). 71 | 72 | Normalmente construir este tipo de sistema apresentam alguns dos casos: 73 | 74 | - Dificuldade para programar, mas robusto e com boa performance (como escrever seus servidores web do zero em C) 75 | - Facilidade para programar, mas não muito robusto/rápido (como quando alguem tenta fazer upload de um arquivo de 5GB e seu servidor trava) 76 | 77 | O objetivo do Node é oferecer um balanço entre estes dois: relativamente fácil para entender e usar, e rápido o suficiente para a maioria dos casos. 78 | 79 | Node não é nenhuma das coisas a seguir: 80 | 81 | - Um framework web (como Rails ou Django, embora possa ser usado para fazer tais coisas) 82 | - Uma linguagem de programação (Node usa JavaScript, mas não é uma linguagem por si só) 83 | 84 | Em vez disso, Node é uma coisa no meio. Node é: 85 | 86 | - Desenhado para ser simples e relativamente fácil de entender e usar 87 | - Útil para programas baseados no I/O que precisam ser rápidos e/ou manusear várias conexões 88 | 89 | Em um nível mais baixo, Node pode ser descrito como uma ferramenta para escrever dois maiores tipos de programas: 90 | 91 | - Programas de rede usando os protocolos da web: HTTP, TCP, UDP, DNS e SSL 92 | - Programas que lêem e escrevem dados em sistemas de arquivos e os processos/memória local 93 | 94 | O que é um "Programa baseado em I/O"? Aqui estão alguns usos comuns: 95 | 96 | - Bancos de dados (ex: MySQL, PostgreSQL, MongoDB, Redis, CouchDB) 97 | - APIs (ex: Twitter, Facebook, Apple Push Notifications) 98 | - Conexões HTTP/WebSocket (usuários de um aplicativo web) 99 | - Arquivos (redimensionador de imagem, editor de vídeo, rádio via internet) 100 | 101 | Node processa I/O de modo assíncrono([asynchronous](http://en.wikipedia.org/wiki/Asynchronous_I/O)) que permite manusear várias coisas diferentes simultaneamente. Por exemplo, se você for a um fast food e pedir um cheesburger eles vão pegar seu pedido imediatamente e então fazer você esperar até que o cheesburger esteja pronto. Neste tempo eles podem pegar outros pedidos e começar a fazer os cheesburgers para outras pessoas. Imagine que você tem que esperar na fila, bloqueando todas as outras pessoas na fila enquanto eles preparam o seu hamburger! Isto é chamado **I/O bloqueante** porque todo o I/O (preparamento dos chessburgers) acontece um de cada vez. Node, por outro lado, é **não-bloqueante**, o que significa que pode preparar vários chessburgers de uma só vez. 102 | 103 | Aqui estão algumas coisas divertidas feitas de forma fácil com Node graças a sua natureza não-bloqueante: 104 | 105 | - Controlar [voos de quadricópteros](http://nodecopter.com) 106 | - Escrever bots para IRC 107 | - Criar [robôs bípedes que andam](http://www.youtube.com/watch?v=jf-cEB3U2UQ) 108 | 109 | ## Módulos do núcleo 110 | 111 | Em primeiro lugar eu recomendo que você instale o Node no seu computador. A maneira mais fácil é visitando [Nodejs.org](http://nodejs.org) e clicar em `install`. 112 | 113 | Node tem um pequeno grupo de módulos no seu núcleo (geralmente chamado de *node core*) os quais são apresentados como uma API pública que tem como objetivo escrever programas com eles. Para trabalhar com sistema de arquivos temos o módulo `fs` e para redes existem os módulos `net` (TCP), `http`, `dgram` (UDP). 114 | 115 | Em adição aos módulos `fs` e de rede existem outros módulos no núcleo do Node. Existe um módulo para resolver consultas DNS de modo assíncrono chamado `dns`, um módulo para pegar informações específicas do sistema operacional como o *tmpdir* chamado `os`, um módulo para alocação de pedaços binários de memória chamado `buffer`, alguns módulos para parsear urls e caminhos (`url`, `querystring`, `path`), etc. A maioria, se não todos os módulos no núcleo do Node, estão ali para suportar os principais casos de uso do Node: escrever rápidos programas que se comunicam com sistemas de arquivos ou redes. 116 | 117 | Node manipula I/O com: callbacks, eventos, streams e módulos. Se você aprender como estas quatro coisas funcionam, então você será capaz de ir dentro de qualquer módulo no núcleo do Node e entender basicamente como interagir com eles. 118 | 119 | ## Callbacks 120 | 121 | Este é o tópico mais importante para entender se você quiser entender como usar o Node. Quase tudo em Node usa callbacks. Eles não foram inventados pelo Node, eles são apenas parte da linguagem JavaScript. 122 | 123 | Callbacks são funções executadas de modo assíncrono, ou posteriormente. Ao invés do código ser lido de cima para baixo de forma procedural, programas assíncronos podem executar diferentes funções em diferentes momentos baseando-se na ordem e velocidade em que as funções declaradas anteriormente (como requisições HTTP ou leituras de sistemas de arquivo) forem acontecendo. 124 | 125 | A diferença pode ser confusa uma vez que determinar se uma função será assíncrona ou não depende muito do contexto. Aqui está um simples exemplo síncrono, você lê de cima para baixo assim como um livro: 126 | 127 | ```js 128 | var myNumber = 1 129 | function addOne() { myNumber++ } // define a função 130 | addOne() // roda a função 131 | console.log(myNumber) // resultado: 2 132 | ``` 133 | 134 | Este código define uma função e então na próxima linha chama a função, sem esperar por nada. Quando a função é chamada imediatamente adiciona 1 para a variável number, então podemos esperar que após a chamada da função number seja 2. Esta é a expectativa de uma código síncrono - De cima para baixo sequencialmente. 135 | 136 | Node, entretanto, usa principalmente código assíncrono. Vamos usar Node para ler nosso número de um arquivo chamado `number.txt`: 137 | 138 | ```js 139 | var fs = require('fs') // require é uma função especial fornecida pelo Node 140 | var myNumber = undefined // nós ainda não sabemos o número já que ele está armazenado em um arquivo 141 | 142 | function addOne() { 143 | fs.readFile('number.txt', function doneReading(err, fileContents) { 144 | myNumber = parseInt(fileContents) 145 | myNumber++ 146 | }) 147 | } 148 | 149 | addOne() 150 | 151 | console.log(myNumber) // resultado: Undefined, já que esta parte do código rodou mais rápido que a função 152 | ``` 153 | 154 | Porque nosso resultado foi `undefined` quando damos *log* no nosso número desta vez? Neste código nós usamos o método `fs.readFile`, que é um método assíncrono. Normalmente coisas que tem que se comunicar com discos rígidos ou redes são assíncronos. Se eles tem que acessar coisas na memória ou fazer algum trabalho na CPU eles serão síncronos. A razão para isso é que I/O é muitoooo muitoooo devagaaaar. Se comunicar com um disco rígido é cerca de 100,000 vezes mais devagar do que se comunicar com a memória (RAM). 155 | 156 | Quando nós rodamos este programa todas as funções são automaticamente definidas, mas elas não são executadas imediatamente. Isto é uma coisa fundamental para entender sobre programas assíncronos. Quando `addOne` é chamado fora de `readFile` então executa a próxima coisa que está pronta para executar. Se não tem nada para executar, Node vai esperar as operações pendentes de fs/rede para terminar ou parar de rodar e sair da linha de comando. 157 | 158 | Quando `readFile` está pronto para ler o arquivo (isto pode levar de milissegundos para segundos ou minutos dependendo do quão rápido o disco rígido é) ele vai rodar a função `doneReading` e mostrar um erro (se existir algum erro) e o conteúdo do arquivo. 159 | 160 | A razão pelo resultado ser `undefined` é que em nenhum lugar do nosso código existe uma lógica que diga para `console.log` esperar até que `readFile` tenha terminado. 161 | 162 | Se você tem algum código que tenha que ser executado várias vezes ou depois de um tempo o primeiro passo é colocar o código dentro de uma função. Depois você chama a função quando você quiser rodar aquele código. Dar nomes descritivos as suas funções ajuda. 163 | 164 | Callbacks são apenas funções que são executados depois de um tempo. A chave para entender callbacks é perceber que eles são usados quando você não sabe **quando** algum código assíncrono vai terminar, mas você sabe **onde** a operação vai terminar - a última linha da função assíncrona! A ordem de cima à baixo que você declara callbacks não necessariamente importa, apenas a lógica hierárquica de assentamento do código. Primeiro você quebra seu código em funções, e depois usa callbacks para declarar se uma função depende do término de outra função. 165 | 166 | O método `fs.readFile` é fornecido pelo Node, é assíncrono e leva um bom tempo para terminar. Considerando o que ele faz: ele tem que ir ao Sistema Operacional, que por sua vez tem que ir ao sistema de arquivos, que está no disco rígido que pode ou não estar rodando à milhares de vezes por minuto. Então ele tem que usar um laser para ler os dados e enviar de volta através das camadas para seu programa JavaScript. Você dá à `readFile` uma função (conhecida como callback) que vai ser chamado assim que receber os dados do sistema de arquivos. Ele bota os dados em uma variável e chama sua função (callback) com aquela variável, nesse caso a variável é chamada `fileContents` porque ela contém os dados do arquivo que foi lido. 167 | 168 | Pense no exemplo do fast food no começo deste tutorial. Em muitos restaurantes você pega um número para colocar em sua mesa enquanto você espera sua comida. Isto usa vários callbacks. Eles vão falar ao servidor o que fazer depois que seu cheesburger fique pronto. 169 | 170 | Vamos colocar nosso `console.log` em uma função e chamar ela como callback. 171 | 172 | ```js 173 | var fs = require('fs') 174 | var myNumber = undefined 175 | 176 | function addOne(callback) { 177 | fs.readFile('number.txt', function doneReading(err, fileContents) { 178 | myNumber = parseInt(fileContents) 179 | myNumber++ 180 | callback() 181 | }) 182 | } 183 | 184 | function logMyNumber() { 185 | console.log(myNumber) 186 | } 187 | 188 | addOne(logMyNumber) 189 | ``` 190 | 191 | Agora a função `logMyNumber` pode ser passada no argumento que vai ser a variável `callback` dentro da função `addOne`. Depois que `readFile` terminar a variável `callback` vai ser invocada (`callback()`). Apenas funções podem ser invocadas, então se você passar alguma coisa que não é uma função vai causar um erro. 192 | 193 | Quando uma função é invocada no JavaScript, o código dentro dela será executado imediatamente. Nesse caso, nosso log será executado já que `callback` é a função `logMyNumber`. Lembre que, só porque você *definiu* uma função não significa que ela será executada. Você tem que *invocar* a função para isso acontecer. 194 | 195 | Para quebrar o exemplo acima em pedaços, aqui está a linha do tempo dos eventos que acontecem quando rodamos este programa: 196 | 197 | - 1: o código é analisado, o que significa que se existir algum erro de sintaxe o programa vai quebrar. Durante esta fase inicial tem 4 coisas que são definidas: `fs`, `myNumber`, `addOne`, e `logMyNumber`. Note que eles estão sendo apenas definidos, nenhuma função foi chamada/invocada ainda. 198 | - 2: quando a última linha do nosso programa é executada `addOne` é invocado, onde `logMyNumber` é passado como `callback`, que é o que queremos que seja chamado quando `addOne` terminar. Isto imediatamente executa o código assíncrono `fs.readFile`. Esta parte do programa leva um tempo para terminar. 199 | - 3: sem nada para fazer, o Node espera até que `readFile` termine. Se existisse alguma outra coisa durante esse tempo, Node poderia fazer o seu trabalho. 200 | - 4: `readFile` termina e chama o callback, `doneReading`, que incrementa o número e imediatamente invoca a função callback de `addOne`, `logMyNumber`. 201 | 202 | Talvez a parte mais confusa de programar com callbacks é que funções são apenas objetos que podem ser armazenadas em variáveis e passadas no programa com diferentes nomes. Dar um simples e descritivo nome para suas variáveis é importante para fazer seu código legível para outras pessoas. Geralmente, falando em programas Node, quando você vê uma variável como `callback` ou `cb` você sabe que é uma função. 203 | 204 | Você pode ter ouvido os termos "programação evencionada" ou "ciclo de eventos". Eles se referem a maneira que `readFile` é implementado. O Node roda primeiro a operação `readFile` e então espera por `readFile` para enviar um evento dizendo que está completo. Enquanto espera, o Node pode checar outras coisas. Dentro do Node está uma lista de coisas que são executadas mas não foram reportadas de volta ainda, então o Node faz um loop contínuo na lista para checar se elas terminaram. Depois que eles terminam eles são "processados" (ex.: callbacks que dependem desse término vão ser invocados). 205 | 206 | Aqui temos a versão de um pseudocódigo do exemplo acima: 207 | 208 | ```js 209 | function addOne(thenRunThisFunction) { 210 | waitAMinuteAsync(function waitedAMinute() { 211 | thenRunThisFunction() 212 | }) 213 | } 214 | 215 | addOne(function thisGetsRunAfterAddOneFinishes() {}) 216 | ``` 217 | 218 | Imagine que você tem 3 funções assíncronas `a`, `b` e `c`. Cada uma leva um minuto para rodar e quando terminadas chamam um callback (que é passado no primeiro argumento). Se você disser para o Node: 'comece executando "a", depois execute "b" quando "a" terminar, e então execute "c" quando "b" terminar', isso ficaria assim: 219 | 220 | ```js 221 | a(function() { 222 | b(function() { 223 | c() 224 | }) 225 | }) 226 | ``` 227 | 228 | Quando este código é executado, `a` vai iniciar automaticamente, então, um minuto depois ele vai terminar e chamar `b`, e um minuto depois ele vai terminar e chamar `c` e, finalmente, 3 minutos depois o Node vai parar o código já que não tem mais nada para fazer. Definitivamente, existem formas mais elegantes de escrever o código acima, mas o ponto é que se você tiver um código que tem que esperar outro código assíncrono terminar então você expressa esta dependência colocando este código em funções que são passadas como callbacks. 229 | 230 | A forma como o Node trabalha requer que você pense de uma forma não-linear. Considerando esta lista de operações: 231 | 232 | ``` 233 | ler um arquivo 234 | processar esse arquivo 235 | ``` 236 | 237 | Se você precisar transformar em pseudocódigo ficaria assim: 238 | 239 | ``` 240 | var file = readFile() 241 | processFile(file) 242 | ``` 243 | 244 | Este tipo de código linear (passo-a-passo, em ordem) não é a maneira que Node trabalha. Se este código fosse executado então `readFile` e `processFile` iriam executar ao mesmo tempo. Isto não faz sentido já que `readFile` vai levar um tempo para completar. Ao invés disso você precisa expressar que `processFile` depende de `readFile`. Este é o trabalho dos callbacks! E por causa da maneira que JavaScript trabalha você pode escrever esta dependência de várias maneiras diferentes: 245 | 246 | ```js 247 | var fs = require('fs') 248 | fs.readFile('movie.mp4', finishedReading) 249 | 250 | function finishedReading(error, movieData) { 251 | if (error) return console.error(error) 252 | // faça algo com movieData 253 | } 254 | ``` 255 | 256 | Mas você támbem pode estruturar o seu código dessa maneira, e ainda assim vai funcionar: 257 | 258 | ```js 259 | var fs = require('fs') 260 | 261 | function finishedReading(error, movieData) { 262 | if (error) return console.error(error) 263 | // faça algo com movieData 264 | } 265 | 266 | fs.readFile('movie.mp4', finishedReading) 267 | ``` 268 | 269 | Ou até mesmo assim: 270 | 271 | ```js 272 | var fs = require('fs') 273 | 274 | fs.readFile('movie.mp4', function finishedReading(error, movieData) { 275 | if (error) return console.error(error) 276 | // faça algo com movieData 277 | }) 278 | ``` 279 | 280 | ## Eventos 281 | 282 | No Node, se você requisitar o módulo [events](http://nodejs.org/api/events.html), você pode utilizar o também chamado "emissor de evento" que o próprio Node utiliza para todas as suas APIs a fim de emitir coisas. 283 | 284 | Eventos são padrões comuns na programação, para conhecer melhor procure por ['observer pattern'](http://en.wikipedia.org/wiki/Observer_pattern) ou 'pub/sub' (publicar/assinar). Assim como callbacks são uma relação de um-para-um entre algo que espera pelo callback e outra parte que chama o callback, eventos seguem o mesmo padrão com exceção de que eles são uma API de muitos-para-muitos. 285 | 286 | A forma mais fácil de pensar a respeito de eventos é que eles permitem a você assinar as coisas. Você pode dizer "quando X fazer Y", enquanto que com um simples callbacks é "faça X, então Y". 287 | 288 | Aqui temos casos comuns para utilizar eventos ao invés de simples callbacks: 289 | 290 | - Uma sala de chat onde você tem um canal de mensagens com muitos ouvintes. 291 | - Servidor de um jogo que necessita saber quando os players se conectam, desconectam, movem-se, atiram ou pulam. 292 | - Mecânismo de um jogo onde você quer permitir que os desenolvedores de jogos disparem eventos como: `.on('jump', function() {})`. 293 | - Um servidor web de baixo nível que quer expor uma API para criar facilmente um gancho para os eventos que acontecem como `on ('incomingRequest')` ou `on ('SERVERERROR')`. 294 | 295 | Se você tentar escrever um servidor de chat que se conecte usando apenas callbacks ele vai se parecer com isso: 296 | 297 | ```js 298 | var chatClient = require('my-chat-client') 299 | 300 | function onConnect() { 301 | // exibe a UI quando conectar-se 302 | } 303 | 304 | function onConnectionError(error) { 305 | // exibe um erro para o usuário 306 | } 307 | 308 | function onDisconnect() { 309 | // avisa ao usuario que ele foi desconectado 310 | } 311 | 312 | function onMessage(message) { 313 | // exibe a mensagem na UI da sala 314 | } 315 | 316 | chatClient.connect( 317 | 'http://mychatserver.com', 318 | onConnect, 319 | onConnectionError, 320 | onDisconnect, 321 | onMessage 322 | ) 323 | ``` 324 | 325 | Como você pode ver, isto é realmente pesado pois você tem que passar todas as funções em uma ordem especifica para a função `.connect`. Escrevendo isso com eventos irá se parecer com isso: 326 | 327 | ```js 328 | var chatClient = require('my-chat-client').connect() 329 | 330 | chatClient.on('connect', function() { 331 | // exibe a UI quando conectar-se 332 | }) 333 | 334 | chatClient.on('connectionError', function() { 335 | // exibe um erro para o usuário 336 | }) 337 | 338 | chatClient.on('disconnect', function() { 339 | // avisa ao usuario que ele foi desconectado 340 | }) 341 | 342 | chatClient.on('message', function() { 343 | // exibe a mensagem na UI da sala 344 | }) 345 | ``` 346 | 347 | Esta abordagem é bastante similar a utilização com callbacks-puros, mas essa abordagem introduz o método `.on` onde atrela um callback a um evento. Isso significa que você pode escolher quais eventos deseja assinar a partir do `chatClient`. Você pode assinar o mesmo evento diversas vezes com diferentes callbacks: 348 | 349 | ```js 350 | var chatClient = require('my-chat-client').connect() 351 | chatClient.on('message', logMessage) 352 | chatClient.on('message', storeMessage) 353 | 354 | function logMessage(message) { 355 | console.log(message) 356 | } 357 | 358 | function storeMessage(message) { 359 | myDatabase.save(message) 360 | } 361 | ``` 362 | 363 | ## Streams 364 | 365 | Logo no início do projeto do Node, as APIs de arquivos de sistema e redes tiveram os seus próprios padrões de separação para lidar com a streaming de I/O. Por exemplo, arquivos em um sistema de arquivos tem propriedades que se chamam "descritores de arquivo" então o módulo `fs` teve uma lógica adicional enquanto o módulo de rede não teve esse conceito adicionado. Apesar de diferenças menores na semâtica como esta, em um nível fundamental ambos os grupos de código tem uma grande quantidade de funcionalidades duplicadas onde fazem a leitura de dados na entrada e saida. O time que esta trabalhando no Node percebeu que seria confuso ter que aprender dois conjuntos de semântica, essencialmente, fazendo a mesma coisa, por isso fizeram uma nova API chamada `Stream` e tudo o que demanda rede e sistema de arquivos usa ela como base. 366 | 367 | O ponto principal do Node é facilitar a comunicaçãoo com o sistema de arquivos e redes através de um padrão que é utilizado em todos os lugares. A boa notícia é que a maioria dos padrões como esse (há apenas alguns) foram descobertos até este ponto e terão poucas mudanças mesmo que seja quase improvável que isso aconteça no futuro. 368 | 369 | Já existem duas grandes fontes que você pode utilizar para aprender a respeito de Streams no Node. Uma é o *stream-adventure* (veja a seção [Aprenda Node de forma interativa](#aprenda-node-de-forma-interativa)) e a outra é uma referência chamada *Stream Handbook*. 370 | 371 | ### Stream Handbook 372 | 373 | O [stream-handbook](https://github.com/substack/stream-handbook#introduction) é um guia, similar a este, que contém referências para tudo o que você quer saber a respeito de Streams. 374 | 375 | [![stream-handbook](stream-handbook.png)](https://github.com/substack/stream-handbook) 376 | 377 | ## Módulos e NPM 378 | 379 | O núcleo do Node é composto de cerca de duas dezenas de módulos, alguns com níveis mais baixos como `events` e `streams`, e outras de níveis mais alto como `http` e `crypto`. 380 | 381 | Este projeto é intencional. O núcleo do Node foi desenvolvido para ser pequeno e os módulos no núcleo devem focar no fornecimento de ferramentas para trabalhar com protocolos e formatos comuns de I/O de maneira multiplataforma. 382 | 383 | Para todo o restante, existe o [NPM](https://npmjs.org/). Qualquer um pode criar um novo módulo para o Node que adicione alguma funcionalidade e publicá-lo no NPM. No momento em que escrevo isso, existem 34.000 módulos no NPM. 384 | 385 | ### Como encontrar um módulo 386 | 387 | Imagine que você está tentando converter arquivos PDF em arquivos TXT. A melhor forma para iniciar esta busca é com `npm search pdf`: 388 | 389 | ![pdfsearch](npm-search.png) 390 | 391 | Há uma tonelada de resultados! NPM é bastante popular e normalmente você vai ser capaz de encontrar várias soluções possíveis. Se você passar por cada módulo e filtrar os resultados em um conjunto mais estreito (filtrando as coisas como módulos de geração de PDF), você vai acabar com estes resultados: 392 | 393 | - [hummus](https://github.com/galkahana/HummusJS/wiki/Features) - c++ pdf manipulator 394 | - [mimeograph](https://github.com/steelThread/mimeograph) - api on a conglomeration of tools (poppler, tesseract, imagemagick etc) 395 | - [pdftotextjs](https://npmjs.org/package/pdftotextjs) - wrapper around [pdftotext](https://en.wikipedia.org/wiki/Pdftotext) 396 | - [pdf-text-extract](https://npmjs.org/package/pdf-text-extract) - another wrapper around pdftotext 397 | - [pdf-extract](https://npmjs.org/package/pdf-extract) - wrapper around pdftotext, pdftk, tesseract, ghostscript 398 | - [pdfutils](https://npmjs.org/package/pdfutils) - poppler wrapper 399 | - [scissors](https://npmjs.org/package/scissors) - pdftk, ghostscript wrapper w/ high level api 400 | - [textract](https://npmjs.org/package/textract) - pdftotext wrapper 401 | - [pdfiijs](https://github.com/fagbokforlaget/pdfiijs) - pdf to inverted index using textiijs and poppler 402 | - [pdf2json](https://github.com/modesty/pdf2json/blob/master/readme.md) - pure js pdf to json 403 | 404 | Diversos módulos possuem sobreposição de funcionalidade, mas as atuais APIs alternativas e a maioria delas requer dependências externas (como o `apt-get install poppler`). 405 | 406 | Aqui estão algumas maneiras diferentes de interpretar os módulos: 407 | 408 | - `pdf2json` é o único que está escrito em JavaScript puro, o que significa que é mais fácil de instalar, especialmente em dispositivos de baixa potência, como o Raspberry Pi ou no Windows, onde o código nativo pode não ser multiplataforma; 409 | - módulos como `mimeograph`, `hummus` e `pdf-extract` onde cada um combina vários módulos de nível inferior para expor uma API de alto nível; 410 | - uma série de módulos parecem tomar como base a linha de comando unix do `pdftotext`/`poppler` 411 | 412 | Vamos comparar as diferenças entre `pdftotextjs` e `pdf-text-extract`, ambos estão contidos no utilitário `pdftotext`. 413 | 414 | ![pdf-modules](pdf-modules.png) 415 | 416 | Ambos: 417 | 418 | - foram atualizados recentemente 419 | - possuem repositórios github "linkados" (isto é muito importante!) 420 | - posuem READMEs 421 | - possuem, ao menos, um número considerável de pessoas o instalando toda semana 422 | - são "livremente" licenciados (qualquer um pode usar) 423 | 424 | Olhando apenas para os arquivos `package.json` + módulo de estatísticas, é difícil obter uma sensação sobre qual pode ser a escolha certa. Vamos comparar os READMEs: 425 | 426 | ![pdf-readmes](pdf-readmes.png) 427 | 428 | Ambos possuem descrições simples, emblemas para CI, instruções de instalação, exemplos claros e instruções para a execução dos testes. Otimo! Mas, qual é que vamos usar? Vamos comparar o código: 429 | 430 | ![pdf-code](pdf-code.png) 431 | 432 | `pdftotextjs` possue cerca de 110 linhas, e `pdf-text-extract` cerca de 40 linhas, mas ambos, essencialmente, resumem-se a esta linha: 433 | 434 | ``` 435 | var child = shell.exec('pdftotext ' + self.options.additional.join(' ')); 436 | ``` 437 | 438 | Será que isto faz um melhor que o outro? Difícil dizer! É realmente importante "ler" o código e fazer as suas próprias conclusões. Se você encontrar um módulo que você gosta, use `npm star modulename` para dar um *feedback* sobre os módulos que você teve uma experiência positiva com ele. 439 | 440 | ### Fluxo de Desenvolvimento Modular 441 | 442 | NPM é diferente da maioria dos gerenciadores de pacotes já que ele instala módulos em uma pasta dentro de outros módulos já existentes. A frase anterior pode não fazer sentido agora, mas é a chave para o sucesso do NPM. 443 | 444 | Muitos gerenciadores de pacotes instalam as coisas globalmente. Por exemplo, se você executar `apt-get install couchdb` no Linux Debian ele vai tentar instalar a última versão estável do CouchDB. Se você estiver tentando instalar o CouchDB como dependência de um Software e este precisa de uma versão anterior do CouchDB, você terá de desinstalar a nova versão do CouchDB e assim instalar a versão anterior. Você não pode ter duas versões do CouchDB instaladas pois o Debian só sabe como instalar as coisas em um local somente. 445 | 446 | Não é apenas o Debian que faz isso. A maioria dos gerenciadores de pacotes de linguagem de programação funciona dessa maneira também. Para organizar o problema das dependências globais descrito acima, foram desenvolvidos ambientes virtuais como [virtualenv](http://docs.python-guide.org/en/latest/dev/virtualenvs.html) para o Python ou [bundler](http://bundler.io/) para o Ruby. Eles apenas dividem o ambiente em muitos ambientes virtuais, um para cada projeto, mas dentro de cada ambiente as respectivas dependências continuam instaladas de modo global. Os ambientes virtuais nem sempre resolvem o problema, às vezes eles só o multiplicam através da inclusão de camadas adicionais de complexidade. 447 | 448 | Com o NPM, a instalação de módulos globais é um anti-padrão. Assim como você não deve usar as variáveis ​​globais em seus programas em JavaScript você também não deve instalar os módulos globais (a menos que você precise de um módulo com um binário executável a aparecer em seu `PATH` global, mas nem sempre você precisa fazer isso - mais sobre isso depois). 449 | 450 | #### Como o `require` funciona. 451 | 452 | Quando você chama o `require('algum_modulo')` no Node, isto é o que acontece: 453 | 454 | 1. se o arquivo chamado `algum_modulo.js` existir no diretório atual o Node vai carregá-lo, do contrário: 455 | 2. o Node vai procurar no diretório atual pela pasta `node_modules` com `algum_modulo` nele 456 | 3. caso não encontre, ele fará o mesmo processo no diretório pai 457 | 458 | Este ciclo será efetuado até o Node chegar no diretório raiz do sistema de arquivos, até que ele comece a verificar os diretórios dos módulos globais (ex.: `/usr/local/node_modules` no Mac) e, mesmo assim, se `algum_modulo` não for encontrado, será lançada uma exceção. 459 | 460 | Aqui temos um exemplo prático: 461 | 462 | ![mod-diagram-01](mod-diagram-01.png) 463 | 464 | Quando o diretório de trabalho atual é `subsubfolder` e `require('foo')` é chamado, o Node vai procurar pelo diretório chamado `subsubfolder/node_modules`. Neste caso, não será encontrado - o diretório foi nomeado incorretamente como `my_modules`. Então, o Node vai procurar no diretório pai e tentar novamente, o que significa que ele vai procurar no diretório `subfolder_B/node_modules` , o qual também não existe. Já a terceira tentativa funciona perfeitamente, pois `folder/node_modules` existe e possui uma pasta chamada `foo` dentro dela. Se `foo` não estivesse neste local, o Node continuaria a sua busca no diretório pai do diretório atual onde a busca está sendo feita. 465 | 466 | Observe que se chamarmos do diretório `subfolder_B` o Node nunca vai encontar `subfolder_A/node_modules`, ele apenas pode visualizar `folder/node_modules` e o que estiver nos diretórios acima dele. 467 | 468 | Um dos benefícios da abordagem do NPM é que os módulos podem instalar seus módulos dependentes em versões específicas de trabalho conhecidos. Neste caso, o módulo `foo` é muito popular - existem três cópias dele, cada um instalado dentro de um diretório parente. A razão para isto é que cada módulo parente necessita de uma versão diferente de `foo`, para exemplificar: `folder` precisa de `foo@0.0.1`, `subfolder_A` precisa de `foo@0.2.1` e assim por diante. 469 | 470 | Isto é o que acontece quando consertamos o nome do diretório `my_modules` colocado equivocadamente para o nome correto `node_modules`: 471 | 472 | ![mod-diagram-02](mod-diagram-02.png) 473 | 474 | Para testar qual módulo foi carregado pelo node, você pode utilizar o comando `require.resolve('algum_modulo')`, o que vai mostrar o caminho para o módulo que o Node encontrar como resultado no procedimento de busca através dos diretórios. O `require.resolve` pode ser útil para fazer uma verificação mais rígida quando você quer ter a certeza de que um módulo está sendo carregado - às vezes há uma outra versão do mesmo módulo mais perto de seu diretório de trabalho atual do que aquele que você pretende carregar. 475 | 476 | ### Como criar um módulo 477 | 478 | Agora que você já sabe como encontrar os módulos e fazer as suas requisições, você pode começar a escrever os seus próprios módulos. 479 | 480 | #### O módulo mais simples possível 481 | 482 | Os módulos do Node são radicalmente leves. Aqui está uma das possibilidades mais simples para um módulo no Node: 483 | 484 | `package.json`: 485 | ```js 486 | { 487 | "name": "number-one", 488 | "version": "1.0.0" 489 | } 490 | ``` 491 | 492 | `index.js`: 493 | ```js 494 | module.exports = 1 495 | ``` 496 | 497 | Por padrão, o Node tenta carregar `module/index.js` quando você faz um `require('module')`, qualquer outro nome de arquivo não vai funcionar a menos que você defina o campo `main` no `package.json` e aponte para ele. 498 | 499 | Coloque ambos os arquivos em uma pasta chamada `number-one` (o `id` em `package.json` deve coincidir com o nome da pasta) e você terá um módulo Node para trabalhar. 500 | 501 | Chamando a função `require('number-one')` será retornado o valor de qualquer `module.exports` que estiver definido dentro do módulo: 502 | 503 | ![simple-module](simple-module.png) 504 | 505 | Uma forma ainda mais rápida para criar um módulo é executando os seguintes comandos: 506 | 507 | ```sh 508 | mkdir my_module 509 | cd my_module 510 | git init 511 | git remote add git@github.com:yourusername/my_module.git 512 | npm init 513 | ``` 514 | 515 | Executando `npm init` será criado um manifesto JSON válido chamado `package.json` para você e se você executá-lo em um repositório `git` existente, será definido um campo `repositories` dentro do `package.json` automaticamente! 516 | 517 | #### Adicionando dependências 518 | 519 | Um módulo pode listar outros módulos a partir do NPM ou Github no campo `dependencies` do arquivo `package.json`. Para instalar o módulo `request` como uma nova dependência e automaticamente adicioná-la ao `package.json` é preciso executar o seguinte comando a partir do seu diretório raiz: 520 | 521 | ```sh 522 | npm install request --save 523 | ``` 524 | 525 | Isto vai instalar uma cópia de `request` na pasta mais próxima do `node_modules` e fazer o nosso `package.json` se parecer assim: 526 | 527 | ``` 528 | { 529 | "id": "number-one", 530 | "version": "1.0.0", 531 | "dependencies": { 532 | "request": "~2.22.0" 533 | } 534 | } 535 | ``` 536 | 537 | Por padrão, `npm install` vai pegar a versão mais atual publicada do módulo. 538 | 539 | ## Desenvolvimento no lado do cliente com NPM 540 | 541 | Um equívoco comum sobre NPM é que uma vez que tem "Node" no nome que deve ser usado apenas para os módulos de JS do lado do servidor. Isto é completamente falso! O NPM está realmente para modulos empacotados do Node, por exemplo, módulos que o Node empacota para você. Os próprios módulos podem ser o que você quiser - eles são apenas uma pasta de arquivos contidos em um arquivo `tar.gz`, e um arquivo chamado `package.json` que declara a versão do módulo e uma lista de todos os módulos que são dependências deste módulo (bem como os respectivos números de versão para a versão de trabalho ficar instalado automaticamente). As dependências deste do módulo são apenas módulos, e estes módulos podem ter dependências, etc, etc, etc. 542 | 543 | O [browserify](http://browserify.org/) é um utilitário escrito em Node que tenta converter qualquer módulo do Node em um código que possa ser executado em qualquer *browser*. Nem todos os módulos funcionam (navegadores não podem fazer determinadas coisas como hospedar um servidor HTTP), mas muitos módulos no NPM "vão" funcionar. 544 | 545 | Para testar o NPM no *browser*, você pode utilizar o [RequireBin](http://requirebin.com/), que é um app que eu fiz e aproveita as vantagens do [Browserify-CDN](https://github.com/jesusabdullah/browserify-cdn), que utiliza o *browserfy* por debaixo dos panos, mas retorna uma saída através do HTTP (em vez da linha de comando - que é a forma como é normalmente usado o browserify). 546 | 547 | Tente colocar este código dentro do *RequireBin* e pressione o botão *preview*: 548 | 549 | ```js 550 | var reverse = require('ascii-art-reverse') 551 | 552 | // makes a visible HTML console 553 | require('console-log').show(true) 554 | 555 | var coolbear = 556 | " ('-^-/') \n" + 557 | " `o__o' ] \n" + 558 | " (_Y_) _/ \n" + 559 | " _..`--'-.`, \n" + 560 | " (__)_,--(__) \n" + 561 | " 7: ; 1 \n" + 562 | " _/,`-.-' : \n" + 563 | " (_,)-~~(_,) \n" 564 | 565 | setInterval(function() { console.log(coolbear) }, 1000) 566 | 567 | setTimeout(function() { 568 | setInterval(function() { console.log(reverse(coolbear)) }, 1000) 569 | }, 500) 570 | ``` 571 | 572 | Ou verifique um [exemplo mais completo](http://requirebin.com/?gist=6031068) (fique a vontade para modificar o código e ver o que acontece): 573 | 574 | [![requirebin](requirebin.png)](http://requirebin.com/embed?gist=6031068) 575 | 576 | ## Evoluindo de forma correta 577 | 578 | Como todo boa ferramenta, o Node é adequado para certos casos de uso. Por exemplo: Rails, o popular web framework, é ótimo para modelar complexas [lógicas de negócios](http://en.wikipedia.org/wiki/Business_logic). 579 | 580 | Exemplo: usando código para representar a vida em um plano objetivado que vivemos físicamente como contas, empréstimos, itinerários e inventários. Embora tecnicamente seja possivel fazer o mesmo utilizando o Node, haveriam desvantagens claras sabendo que o Node é projetado para resolver problemas de I/O e não sabe muito a respeito de "lógica de negócio". Cada ferramenta tem um foco para resolver diferentes problemas. Esperamos que este guia ajude-o a ganhar uma compreensão intuitiva dos pontos fortes do Node para que você saiba quando ele será útil. 581 | 582 | ### O que está fora do escopo do Node? 583 | 584 | Fundamentalmente o Node é somente usado como uma ferramenta para gerenciar I/O ao redor do sitema de arquivos e redes, ele deixa outras funcionalidades mais bonitas com módulos de terceiros. Aqui são algumas das coisas ques estão **fora** do escopo do Node: 585 | 586 | #### Web frameworks 587 | 588 | Existe uma boa quantidade de web frameworks construidos em cima do node (framework é um pacote que tenta resolver um problema de alto nível e problemas similares à modelagem de lógica de negócios), mas o Node não é um framework para web. Frameworks web são escritos para serem utilizados no Node e nem sempre tomam o mesmo tipo de decisões sobre a adição de complexidade, abstração e compreensão que o Node faz e podem ter outras prioridades. 589 | 590 | #### Sintaxe da linguagem 591 | 592 | O Node usa JavaScript e não muda nada sobre isso. Felix Geisendörfer tem um belo conteúdo escrito sobre o "Guia de estilo do Node" [aqui](https://github.com/felixge/node-style-guide). 593 | 594 | #### Abstração da linguagem 595 | 596 | Quando possivel, o Node vai usar a maneira mais simples para fazer algo. Código mais "bonito" faz do seu JavaScript mais complexo e compromissado com vantagens e desvantagens. Programar é difícil, especialmente em JS onde você tem 1000 soluções para o mesmo problema! Essa é a principal razão para o Node optar pela simplicidade sempre que possível e que pode ser uma opção universal. Se você está resolvendo um problema complexo e está insatisfeito com o modo como o Node implementa as coisas com "soluções de JS com gosto de baunilha", sinta-se livre para resolver isso dentro do seu app ou módulo usando quaisquer abstrações que você preferir. 597 | 598 | Um grande exemplo é como o Node usa os callbacks. Logo no início foi experimentado a característica chamada *promises* que adicionava algumas funcionalidades para fazer o código assíncrono parecer mais linear. Ele foi levado para o fora do núcleo do Node por algumas razões: 599 | 600 | - eles são mais complexos que callbacks 601 | - ele podem ser implementados na *userland* (distriuído no npm como módulo de terceiros) 602 | 603 | Considere uma das mais universais e básicas ideias que o Node faz: ler um arquivo. Onde você lê um arquivo e precisa saber onde os erros acontecem, como quando o disco rígido morre no meio da sua leitura. Se Node tivesse *promises* todo mundo teria que criar um *branch* como o código abaixo: 604 | 605 | ```js 606 | fs.readFile('movie.mp4') 607 | .then(function(data) { 608 | // faz algo com os dados 609 | }) 610 | .error(function(error) { 611 | // manipula o erro 612 | }) 613 | ``` 614 | 615 | Isso adiciona uma complexidade desnecessária. No lugar de duas funções separadas o Node somente usa uma única função de callback. Aqui temos as regras: 616 | 617 | - Quando não existir erros passe *null* como primeiro argumento. 618 | - Quando existir um erro, passar ele como primeiro argumento. 619 | - O restante dos argumentos são usados para qualquer coisa (usualmente dados ou respostas, já que na maior parte do tempo o Node está lendo ou escrevendo coisas). 620 | 621 | Por isso, o Node usa o estilo de callback: 622 | 623 | ```js 624 | fs.readFile('movie.mp4', function(err, data) { 625 | // manipula erro, faz algo com os dados 626 | }) 627 | ``` 628 | 629 | #### Soluções baseadas em Threads/fibers/non-event 630 | 631 | Nota: Se você não sabe o que isso tudo significa você terá uma facilidade maior com o tempo para aprender como o Node funciona, visto que desaprender coisas leva o mesmo tempo que aprender. 632 | 633 | O Node usa *threads* internamente para fazer coisas de uma forma rápida mas não expõe isso ao usuário. Se você é um usuário técnico e está perguntando-se o porquê dele ser projetado desta maneira, esta leitura é 100% sobre [o design de libuv](http://nikhilm.github.com/uvbook/), que onde a camada de I/O feita em C++ e pela qual o Node é concebido. 634 | 635 | ## Licença 636 | 637 | ![CCBY](CCBY.png) 638 | 639 | Creative Commons Attribution License (faça o que quiser, apenas dê os créditos) 640 | http://creativecommons.org/licenses/by/2.0/ 641 | 642 | Donate icon is from the [http://thenounproject.com/noun/donate/#icon-No285](Noun Project) 643 | -------------------------------------------------------------------------------- /readme.zh-cn.md: -------------------------------------------------------------------------------- 1 | # Node的艺术 2 | ## Node.js入门 3 | 4 | 本文档假定读者已经懂了以下的两样东西: 5 | 6 | - 懂得至少一种编程语言。例如:JavaScript,Ruby,Python,Perl或其他编程语言。如果你还不是程序员,你不懂编程语言,你可以阅读[JavaScript for Cats](http://jsforcats.com/)。:cat2: 7 | - git和github。这是一个开源的协作工具,Node社区的用户使用git共享模块。你需要懂得基本操作就能了。这里有三篇很好的入门教程:[1](https://github.com/jlord/git-it-electron#readme), [2](http://zachbruggeman.me/github-for-cats/), [3](http://opensourcerer.diy.org/) 8 | 9 | This short book is a work in progress + I don't have a job right now (if I did I wouldn't have the time to write this). If you like it then please consider donating via [gittip](https://www.gittip.com/maxogden/) so that I can write more! 10 | 11 | > 译者: 上面这段我没有翻译,因为我希望保持原文。上面作者提到,目前他还没找到工作。如果你喜欢这个文档,希望你可以通过[gittip](https://www.gittip.com/maxogden/)乐捐给作者。这样作者才能够写更多。 12 | 13 | [![donate](donate.png)](https://www.gittip.com/maxogden/) 14 | 15 | ## 目录 16 | 17 | - [了解Node](#了解Node) 18 | - [核心模块](#核心模块) 19 | - [回调函数](#回调函数) 20 | - [事件](#事件) 21 | - [流](#流) 22 | - [模块](#模块) 23 | - [用npm在客户端开发](#用npm在客户端开发) 24 | - [析薪杝矣](#析薪杝矣) 25 | 26 | 27 | ## 了解Node 28 | 29 | Node.js是一个开源项目,目的是让你通过编写JavaScript的程序进行网络、文件系统或其他I/O源的沟通。就这些!它只是一个简单而稳定的I/O平台,你可以在这个平台上架构模块。 30 | 31 | 有没有I/O出的例子? 我这里有一张图,上面是我用Node.js制作的程序,你可以看到上面有很多I/O源: 32 | 33 | ![server diagram](server-diagram.png) 34 | 35 | 如果你无法明白上图显示的所有东西,这是没问题的。重点是你看到一个Node的运作(在中间六边形那个),它就像经纪人,管理全部I/O的端口(橙色和紫色的线条代表I/O)。 36 | 37 | 一般上我们编写的程序可以分为以下两类: 38 | 39 | - 很难编写,但是效率超高(就像用C从零开始编写一个Web服务器) 40 | - 很简单编写,但是不够效率/强大(就像有人上传5GB的文件去你服务器,但是服务器宕机了) 41 | 42 | Node试图做到平衡在这两者之间:在大多数用列做到高效运行,而且容易明白和开发。 43 | 44 | Node不是以下两样东西: 45 | 46 | - 不是Web框架 (不像Rails或Django,尽管它可以被用来做这样的事情) 47 | - 不是编程语言(Node是使用JavaScript编程,它没有自己的编程语言) 48 | 49 | 相反,Node是: 50 | 51 | - 设计上简单,而且容易明白和使用的平台 52 | - 适合那些需要快速和处理很多I/O链接的程序 53 | 54 | 在基层,Node可以作为一种工具,并编写出以下两类程序: 55 | 56 | - 需要使用到Web协议(如:HTTP、TCP、UDP、DNS和SSL)的网络程序 57 | - 需要对文件系统或者本地进程/内存进行读入和读出操作的程序 58 | 59 | 什么是“I/O程序”? 这里有一些常见的I/O源: 60 | 61 | - 资料库 (如:MySQL、PostgreSQL、MongoDB、Redis、CouchDB) 62 | - APIs(如:Twitter、Facebook、Apple Push Notifications) 63 | - HTTP/WebSocket的链接(从用户的Web应用程序) 64 | - 文件档(图像尺寸伸缩软件、视频编辑软件、网络收音机) 65 | 66 | Node能够[异步处理](http://en.wikipedia.org/wiki/Asynchronous_I/O)多个不同种类的I/O源。比如说,假设你来到快餐店,你向店员要了一个芝士汉堡,他们会马上为你下单和准备汉堡。然后,他们会要求你在旁边等汉堡完成。在你等待这段时间,他们可以接受其他订单和帮其他人准备汉堡。试想下,如果你站在柜台前面,一直等到你的芝士汉堡完成,那么你就阻碍了后面的人下订单,厨师也不能帮其他人准备汉堡!我们称这个为**阻塞I/O**,因为一次只能处理一个I/O操作(厨师一次只能准备一个汉堡)。Node,不是这样的,它是**非阻塞**性质,就是说它能一次准备很多汉堡。 67 | 68 | 多谢Node非阻塞的性质,让我们可以实现以下这么有趣事情: 69 | 70 | - 控制[Quadcopters飞行](http://nodecopter.com) 71 | - 编写IRC谈天机器人 72 | - 制作一个[双脚走路的机器人](http://www.youtube.com/watch?v=jf-cEB3U2UQ) 73 | 74 | ## 核心模块 75 | 76 | 首先,你需要在电脑上安装Node。Node安装很简单,只需浏览[nodejs.org](http://nodejs.org)和点击`Install`. 77 | 78 | Node拥有一组核心模块(通常被称为`Node核心`)提供公共 API 让你编程时候调用。我们可以调用`fs`模块来操作文件系统。当我们要进行网络操作时候,我们会调用网络模块,例如:`net`(TCP),`http`,`dgram`(UDP)。 79 | 80 | 除了`fs`和网络模块之外,Node核心还有很多其他的核心模块。如`dns`模块用来异步解析DNS查询。`os`模块可以用来收集操作系统的资讯,如tempdir的路径。`buffer`模块可以处理二进制数据。还有些模块可以处理URL和路径,如:`url`,`querystring`和`path`等等。大部分的核心模块都支持Node的主要使用目标:快速编写能够进行文件或网络操作的程序。 81 | 82 | Node通过回调,事件,数据流和模块来控制I/O。如果你学会了这四样东西如何工作,那么你就能够灵活使用任何核心模块,而且你还会懂得模块的基本接口。 83 | 84 | ## 回调函数 85 | 86 | 如果想真的弄明白怎么使用Node,回调函数是你需要了解的东西中最重要的,没有之一。回调函数倒不是有了Node后才有的,只不过这功能是JavaScript中尤其好用的一个。 87 | 88 | 回调函数是指非同步执行的,或者是在将来某个时间才会被执行的函数。同步代码运行的顺序是从上至下,而非同步的程序却是在不同的时间运行不同的函数,这些事件都基于某些某同步函数的顺序和运行速度,包括HTTP请求和从文件系统里读取内容等等。 89 | 90 | 这种同步和非同步之间的差异可能会让人比较困惑,因为看一个函数是不是非同步,很大程度上取决于具体的情况。下面是一个很简单的同步函数的例子: 91 | 92 | ```js 93 | var myNumber = 1 94 | function addOne() { myNumber++ } // 定义函数 95 | addOne() // run the function 96 | console.log(myNumber) // 结果显示2 97 | ``` 98 | 99 | 上面的代码定义了一个函数,然后调用了它,之间没有任何停留。当该函数被调用时,它立即把那个数字加上1,所以我们可以预见到,调用过该函数后,那个数字的值会变成2。 100 | 101 | 现在假设我们把数字存在一个叫`number.text`的文件里: 102 | 103 | ```js 104 | var fs = require('fs') // require是Node提供的一个特别函数 105 | var myNumber = undefined // 数字被存在文件里,因此我们并不知道它的值 106 | 107 | function addOne() { 108 | fs.readFile('./number.txt', function doneReading(err, fileContents) { 109 | myNumber = parseInt(fileContents) 110 | myNumber++ 111 | }) 112 | } 113 | 114 | addOne() 115 | 116 | console.log(myNumber) // 结果显示undefined 117 | ``` 118 | 119 | 为什么这些显示出来的值是`undefined`?因为在上面的代码中,我们用了`fs.readFile`这个方法,而它恰好是个非同步方法。一般来说,需要和硬盘沟通或是从通信网络获得数据的,都是非同步的。只是需要从内存里或CPU里读些东西的话,就是同步的。这是因为I/O(输入输出)是非常非常非常慢的。如果要大概形容一下,从硬盘里读取大概比从内存里读取慢了10万倍。 120 | 121 | 当这个程序运行的时候,所有的函数都马上被定义,但它们不是马上都被执行的。这是非同步编程的一个基础概念。当`addOne`被调用的时候,Node执行`readFile`这个方法,但不等到`readFile`结束,它就继续进行下一个不需要等待就能执行的函数了。如果没有可以执行的东西了,Node要么会停下来,等待文件读取或是网络通讯结束,要么就结束运行,返回到命令行。 122 | 123 | 当`readFile`终于把文件读完的时候(需要的时间从几毫秒到几秒到几分钟不等,要看硬盘有多快),Node会执行`doneReading`这个函数,并把报的错(如果读文件的时候有报错的话)和文件的内容传给它。 124 | 125 | 在上面的程序中,之所以会显示`undefined`,是因为我们的代码并没有在任何地方注明了要在文件读取完成后再`console.log`出数字。 126 | 127 | 如果你有一些想要反复执行的代码,你应该做的第一件事就是把这些代码放在一个函数里。然后,在你需要执行那些代码的时候,调用这个函数就好了。你给函数起的名字最好能让人一看就知道这个函数是做什么的。 128 | 129 | 回调函数,不过是在将来某个时间被执行的函数。要理解回调函数,很关键的一点是它被使用的时机。你使用回调函数的前提是,你不知道**什么时候**某个非同步进程会结束,但知道这个进程会在**哪里**结束————就在那个非同步函数的最后一行!你在什么地方声明这些函数并不重要,重要的是这些函数之间的逻辑顺序。把代码分装进各个函数之后,如果一个函数的执行取决于另一个函数何时结束,就该使用回调函数了。 130 | 131 | 上面代码中的`fs.readFile`方法是Node自带的,这个方法是非同步的,而且要花费很长时间。想想看它要做多少事情:它要进入操作系统,进入文件系统,文件系统可是在硬盘上的,硬盘可能转得飞快,也可能根本就不转。然后它要用激光读出数据,并把数据传回你的JavaScript程序。当你给了它一个回调函数后,它就可以在成功从文件系统中取得数据以后,调用那个回调函数。它会把数据放在一个变量里,交给你给的回调函数,我们给这个变量起的名字叫做`fileContents`,因为变量中包含的是读取到的文件内容。 132 | 133 | 想想看这个教程刚开始时的那个餐厅的例子。在很多餐厅,在你点的菜上来之前,服务生会放一个数字牌在你桌上。这个和回调函数很类似。回调函数的作用就是告诉服务器在你的芝士汉堡好了后要做些什么。 134 | 135 | 现在,让我们把`console.log`放进一个函数里作回调函数使用吧。 136 | 137 | ```js 138 | var fs = require('fs') 139 | var myNumber = undefined 140 | 141 | function addOne(callback) { 142 | fs.readFile('./number.txt', function doneReading(err, fileContents) { 143 | myNumber = parseInt(fileContents) 144 | myNumber++ 145 | callback() 146 | } 147 | } 148 | 149 | function logMyNumber() { 150 | console.log(myNumber) 151 | } 152 | 153 | addOne(logMyNumber) 154 | ``` 155 | 156 | 现在`logMyNumber`这个函数可以被传给`addOne`作为回调函数了。在`readFile`完成后,`callback`这个变量会被执行(也就是`callback()`)。只有函数才能被执行,所以如果你提供一个不是函数的东西,程序会出错。 157 | 158 | 在JavaScript里,当函数被调用,其包含的代码会立刻被执行。在这个例子里,`console.log`会被执行,因为`callback`其实就是`logMyNumber`。要记得,你*定义*了一个函数,不代表它会执行!你一定得*调用*它才行。 159 | 160 | 如果要更细地分析一下这个例子,下面是按时间顺序排列的所有发生的事件: 161 | 162 | - 1: 代码被分析,这时,如果有任何语法错误,程序会停止并报错。 163 | - 2: `addOne`被调用,以`logMyName`作为它的回调函数,也就是我们想在`addOne`结束后执行的函数。接下来,非同步的`fs.readFile`马上开始运行。这个部分要花上点时间。 164 | - 3: Node暂时没事做的,于是它就闲下来等待着`readFile`结束。 165 | - 4: `readFile`结束了,`doneReading`这个函数被调用,它把数字加上1然后马上调用回调函数————也就是我们传给`addOne`的`logMyNumber`。 166 | 167 | 也许关于回调函数最难理解的部分是,为什么函数可以被存在变量里被传来传去,而且还有着变来变去的名字。要让你的代码更容易被看懂,给你的函数起简单明了的名字是很重要的一部分。总的来说,在使用Node时,如果你看见一个变量叫做`callback`或是它的缩写`cb`,你差不多可以确定它就是一个函数。 168 | 169 | 你可能听过一个术语叫“事件驱动式编程”,或者叫“事件循环”。`readFile`这类的函数就利用了“事件循环”。Node首先开始运行`readFile`,并等待着`readFile`发回一个事件。在Node等待的这段时间,它可以继续运行其他的东西。在Node里有一个列表,里面记下了所有开始运行却还没有发回结束信号的事,Node就一遍遍循环检查这个列表,看看有没有事情完成了。它们运行完之后,就会被Node处理掉,也就是说,需要运行的回调函数会被运行。 170 | 171 | 下面是上面例子的伪代码写法: 172 | 173 | ```js 174 | function addOne(thenRunThisFunction) { 175 | waitAMinuteAsync(function waitedAMinute() { 176 | thenRunThisFunction() 177 | }) 178 | } 179 | 180 | addOne(function thisGetsRunAfterAddOneFinishes() {}) 181 | ``` 182 | 183 | 假设你有三个非同步函数:`a`、`b`,和`c`。它们要花上一分钟来运行,运行完了之后会调用一个回调函数(函数以第一个参数的形式被传进函数)。如果你想让Node先运行a,a运行完后运行b,b运行完后再运行c,那么程序是下面这样的: 184 | 185 | ```js 186 | a(function() { 187 | b(function() { 188 | c() 189 | }) 190 | }) 191 | ``` 192 | 193 | 当这段代码被运行时,`a`马上就会被运行,一分钟后`a`结束运行,`b`开始执行,再一分钟后,`b`结束运行,`c`开始运行。最后,也就是三分钟后,Node会停止运行,因为所有事都运行完了。上面的代码可能看起来没那么漂亮,但重点是,如果有些代码需要在某些非同步的事情运行完了之后再运行,你需要做的是把那些代码放进一个函数,当作回调函数传给非同步函数,以表示回调函数中的代码要依赖非同步的部分运行结束才能运行。 194 | 195 | Node要求你用非线性的思维思考。看看下面这两件事: 196 | 197 | ``` 198 | read a file 199 | process that file 200 | ``` 201 | 202 | 如果你只是不假思索地把这两件事改成伪代码,你会这么写: 203 | 204 | ``` 205 | var file = readFile() 206 | processFile(file) 207 | ``` 208 | 209 | 这种线性的代码不是Node的风格。(线性是指一步接一步、按照顺序地)。如果上面的代码被运行了。那么`readFile`和`processFile`会同时被调用。这根本说不通,因为`reafFile`要花上一阵子时间才能运行结束。正确的做法是,表达清楚`processFile`是要依赖`readFile`结束才能运行的。这就是回调函数的作用了!因为JavaScript的特点,有好几种方法可以表达这种依赖性: 210 | 211 | ```js 212 | var fs = require('fs') 213 | fs.readFile('movie.mp4', finishedReading) 214 | 215 | function finishedReading(error, movieData) { 216 | if (error) return console.error(error) 217 | // do something with the movieData 218 | } 219 | ``` 220 | 221 | 不过你这样写也可以,照样会成功运行: 222 | 223 | ```js 224 | var fs = require('fs') 225 | 226 | function finishedReading(error, movieData) { 227 | if (error) return console.error(error) 228 | // do something with the movieData 229 | } 230 | 231 | fs.readFile('movie.mp4', finishedReading) 232 | ``` 233 | 234 | 甚至像下面这样: 235 | 236 | ```js 237 | var fs = require('fs') 238 | 239 | fs.readFile('movie.mp4', function finishedReading(error, movieData) { 240 | if (error) return console.error(error) 241 | // do something with the movieData 242 | }) 243 | ``` 244 | 245 | ## 事件 246 | 在Node中如果你加载了[events](http://nodejs.org/api/events.html)模块, 就可以用被称作`event emitter`(事件分发器)的功能。 Node在它的API中使用这一功能分发事件。 247 | 248 | 在编程中运用`事件`是一种常见的方法。它还有一个我们更为熟知的名字[观察者模式](https://zh.wikipedia.org/wiki/%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F),或者`发布/监听`模式。在回调函数的模式中,调用回调函数的命令与等待回调函数的命令间的关系是一一对应的,而在事件模式中这两种命令的关系可以是多对多的。 249 | 250 | 理解事件最简单的方式,就是把它当成一个你监听的东西。如果说在回调函数里面我们的逻辑是`先做X,再做Y`,那么在事件中我们的逻辑是`当X发生时,做Y`。 251 | 252 | 以下是一些常见的用事件取代回调函数的例子: 253 | 254 | 255 | - 需要向所有听众广播的聊天室 256 | - 需要及时了解玩家上线、下线、运动、设计、跳跃等动作的游戏服务器 257 | - 需要能让开发者执行`.on('jump', function() {})`这种命令的游戏引擎 258 | - 能够执行`.on('incomingRequest')` 或 `.on('serverError')`这一API的低端web服务器。 259 | 260 | 如果我们想只用回调函数写一个连接聊天服务器的模块的话,代码会长这样: 261 | 262 | 263 | ```js 264 | var chatClient = require('my-chat-client') 265 | 266 | function onConnect() { 267 | // have the UI show we are connected 268 | } 269 | 270 | function onConnectionError(error) { 271 | // show error to the user 272 | } 273 | 274 | function onDisconnect() { 275 | // tell user that they have been disconnected 276 | } 277 | 278 | function onMessage(message) { 279 | // show the chat room message in the UI 280 | } 281 | 282 | chatClient.connect( 283 | 'http://mychatserver.com', 284 | onConnect, 285 | onConnectionError, 286 | onDisconnect, 287 | onMessage 288 | ) 289 | ``` 290 | 291 | 正如你所见,用回调函数写会变得十分笨拙。你需要把所有的功能函数按特定的顺序传给`.connect`来执行。但是将上面所写的功能用事件来实现,就会变成这样: 292 | 293 | ```js 294 | var chatClient = require('my-chat-client').connect() 295 | 296 | chatClient.on('connect', function() { 297 | // have the UI show we are connected 298 | }) 299 | 300 | chatClient.on('connectionError', function() { 301 | // show error to the user 302 | }) 303 | 304 | chatClient.on('disconnect', function() { 305 | // tell user that they have been disconnected 306 | }) 307 | 308 | chatClient.on('message', function() { 309 | // show the chat room message in the UI 310 | }) 311 | ``` 312 | 313 | 这种写法和回调函数很像,但是运用了高大上的`.on`功能,它会让一个回调函数‘监听’一个事件。 这意味着你可以在`chatClient`中选择任意一个想要监听的事件。 你甚至可以为多个回调函数监听同一个事件: 314 | 315 | ```js 316 | var chatClient = require('my-chat-client').connect() 317 | chatClient.on('message', logMessage) 318 | chatClient.on('message', storeMessage) 319 | 320 | function logMessage(message) { 321 | console.log(message) 322 | } 323 | 324 | function storeMessage(message) { 325 | myDatabase.save(message) 326 | } 327 | ``` 328 | 329 | ## 流 330 | 331 | 在早期的node项目中,文件系统和网络API有各自处理I/O流的方式。比如,在文件系统中,文件有一个‘文件描述器’的东西,因此`fs`模块需要调用额外的逻辑来跟踪这个东西。然而在网络模块中根本没有’xx描述器‘这样的概念。尽管在语义上有像这样较小的区别,在最底层这两种模块(文件系统、网络模块)在重复着同样的数据读写操作。Node的维护们很快意识到这样的重复很容易迷惑开发者,于是他们造了这么个叫`流`(Stream)的东西,使网络与文件系统的代码可以同样工作。 332 | 333 | Node的理念就是以更简单的方式来处理文件系统和网络,所有理所应当的应该有一个通用的模式,可以在不同的场景中运用。好消息是,类似的大多数模式(尽管数量很少)现在已经被认为node在未来不会去更改。 334 | 335 | 已经有两个很棒的资源可以用来学习node的流对象。一个叫‘stream-adventure’(参考‘[了解Node](#了解Node)’部分),另一个叫‘Stream Handbook’。 336 | 337 | ### Stream Handbook 338 | 339 | [stream-handbook](https://github.com/substack/stream-handbook#introduction) 是一个与本项目相似的,包含所有你需要、想要了解的有关流对象的内容的教程。 340 | 341 | [![stream-handbook](stream-handbook.png)](https://github.com/substack/stream-handbook) 342 | 343 | ## 模块 344 | 345 | Node的核心是由许多模块(modules)组成,像底层的[事件](#事件)和[流](#流),高一些层次的`http`和`crypto`。 346 | 347 | Node有意被设计成这样,使它的核心模块轻量化,并注重于提供跨平台的处理普通I/O协议和类型的最基本工具。 348 | 349 | 除此之外,你可以在[npm](https://npmjs.org/)上找到其它需要了解的东西。任何人都可以创建一个新的模块,添加一些功能,并发布到`npm`上。到目前为止,npm上已经有196,950个模块可供下载。 350 | 351 | ### 如何找到心怡的模块 352 | 353 | 想象一下你在试图把一个PDF文件转换成一个TXT文本。最好的方式就是执行这样一个搜索命令`npm search pdf`: 354 | 355 | ![pdfsearch](npm-search.png) 356 | 357 | 这里有数以千计的结果! npm十分热门,所以通常你都可以找到许多可能的解决方案。 如果你把以上的搜索结果浓缩一下(比如过滤掉PDF生成模块),你会得到这样的一些结果: 358 | 359 | - [hummus](https://github.com/galkahana/HummusJS/wiki/Features) - c++ pdf manipulator 360 | - [mimeograph](https://github.com/steelThread/mimeograph) - api on a conglomeration of tools (poppler, tesseract, imagemagick etc) 361 | - [pdftotextjs](https://npmjs.org/package/pdftotextjs) - wrapper around [pdftotext](https://en.wikipedia.org/wiki/Pdftotext) 362 | - [pdf-text-extract](https://npmjs.org/package/pdf-text-extract) - another wrapper around pdftotext 363 | - [pdf-extract](https://npmjs.org/package/pdf-extract) - wrapper around pdftotext, pdftk, tesseract, ghostscript 364 | - [pdfutils](https://npmjs.org/package/pdfutils) - poppler wrapper 365 | - [scissors](https://npmjs.org/package/scissors) - pdftk, ghostscript wrapper w/ high level api 366 | - [textract](https://npmjs.org/package/textract) - pdftotext wrapper 367 | - [pdfiijs](https://github.com/fagbokforlaget/pdfiijs) - pdf to inverted index using textiijs and poppler 368 | - [pdf2json](https://github.com/modesty/pdf2json/blob/master/readme.md) - pure js pdf to json 369 | 370 | 在这之中许多模块都有重复的功能,并且使用了不同的API。很多模块可能会依赖外部的库,你需要先安装这些库(比如 `apt-get install poppler`)才能使用这些模块。 371 | 372 | 以下是对上述这些模块的一些说明: 373 | 374 | - `pdf2json`是唯一一个用纯JavaScript写的模块,所以他没有依赖并且很容易安装。特别是在一些低功耗的设备上,像树莓派,或者像Windoes这样没有跨平台库支持的操作系统。 375 | - `mimeograph`, `hummus` 和`pdf-extract` ,这几个模块集合了许多底层的模块,并抽象出高层的API 376 | - 许多模块实际上都是在unix命令后工具`pdftotext`/`poppler`上搭建的 377 | 378 | 让我们来比较一下`pdftotextjs` 和 `pdf-text-extract`这两个工具,他们都是在`pdftotext`的基础上打包而成的。 379 | 380 | ![pdf-modules](pdf-modules.png) 381 | 382 | 这两个模块: 383 | 384 | - 最近都有更新 385 | - 有github的项目链接(这一点很重要!) 386 | - 有说明文档 387 | - 每周都有一定的新安装用户 388 | - 非常宽松的使用许可(所有人都可以使用) 389 | 390 | 仅依靠`package.json`文件和模块的统计数据很难说哪一个最正确的选择。所以我们来对比一下说明文档吧: 391 | 392 | ![pdf-readmes](pdf-readmes.png) 393 | 394 | 两个文档都有简单的介绍,CI编译通过的标志,安装命令,清晰的例子和一些测试命令。赞!但是我们要选哪一个呢?我们来对比一下代码吧: 395 | 396 | ![pdf-code](pdf-code.png) 397 | 398 | `pdftotextjs` 有110行代码,而`pdf-text-extract`则只有40行。其实这两个模块最核心的操作可以归结为这一行代码: 399 | 400 | ``` 401 | var child = shell.exec('pdftotext ' + self.options.additional.join(' ')); 402 | ``` 403 | 404 | 通过这一点能判断出哪一个更好吗?很难说诶!所以*读*代码再下结论是很重要的。如果你找到了想要的模块,执行`npm star modulename`来给你喜欢的模块一个正面的反馈信息吧。 405 | 406 | ### 模块开发流程 407 | 408 | npm和大多数的包管理软件不同,它会将模块安装在另一个已有模块的目录中。这句话可能很难以理解,但知道这是npm成功的关键就好。 409 | 410 | 许多包管理软件会全局安装。比如你在Debian系统上执行`apt-get install couchdb`,apt-get会试图安装最新的CouchDB。如果你再试图安装一个依赖旧版本CouchDB的软件,你就得卸载掉新的版本,再安装旧版本的CouchDB。你无法同时保留新旧两个版本的CouchDB,因为Debian(apt-get)只知道将软件安到同一个位置。 411 | 412 | 当然这不是Debian一个系统的错,绝大多数语言的包管理软件都这样。 为了解决这种全局依赖的问题,已经有了许多虚拟环境的项目被创建出来。比如针对Python的 [virtualenv](http://python-guide.readthedocs.org/en/latest/dev/virtualenvs/),或者针对Ruby的[bundler](http://bundler.io/)。然而这些只是把你的环境配置划分成不同的虚拟环境,每个工程对应一个,但实际上每个环境配置依旧是全局安装的。而且虚拟环境不总是能解决问题,有时候只是增加了多一层的复杂度。 413 | 414 | 用npm来安装全局模块是反人类的。就像你不应该在你的JavaScript代码中使用全局变量一样。(除非你需要一个可执行的二进制文件集成进`PATH`中,但你不总需要这样做--在后面我们会解释这一点)。 415 | 416 | #### `require`命令是如何工作的 417 | 418 | 当我们加载一个模块的时候,我们调用`require('some_module')`,以下是在node中会发生的事情: 419 | 420 | 1. 如果`some_module.js`文件在当前目录下,node会加载它,否则 421 | 2. node会在当前目录下寻找 `node_modules` 文件夹,然后在其中找`some_module` 422 | 3. 如果还没找到,node会跳到上一层文件夹,然后重复步骤2 423 | 424 | 这一操作会不断循环直到node找到根目录是还没有找的这个模块,在那之后node回去找全局安装时的文件夹(比如Mac OS系统上的 `/usr/local/node_modules`),如果还没有找到这个`some_module`,node会报错。 425 | 426 | 这里有一个上述操作的可视化说明: 427 | 428 | ![mod-diagram-01](mod-diagram-01.png) 429 | 430 | 当前的工作目录为`subsubfolder`,并且`require('foo')`被执行时,node会查找 `subsubsubfolder/node_modules`这个子目录。在这个例子中,由于这个子目录被错误地命名为`my_modules`了,因而node找不到它,只好跳到`subsubfolder`的上一级目录`subfolder_B`寻找`subfolder_B/node_modules`,然而这个文件夹不存在;于是node再往上一级目录寻找,在`subfolder_B`的上一级目录`folder`中找到了`folder/node_modules`,*并且*`foo`文件夹在其中。至此搜索便结束了,但如果`foo`并不在那个目录里,node会继续往上一层目录搜索。 431 | 432 | 注意这点,我们在`subfolder_B`中没找到`foo`模块并向上一级目录寻找的时候,并不会向同一级的 `subfolder_A/node_modules`中寻找。在它的搜索树中只有 `folder/node_modules`。 433 | 434 | 使用npm的一个好处就是,模块可以安装自己依赖的特定版本模块。 在这个例子中,`foo`模块特别流行,以至于我们将三个版本安装在不同位置。这样做的原因是调用它们的模块依赖特定版本的`foo`,比如`folder`依赖`foo@0.0.1`, `subfolder_A` 依赖 `foo@0.2.1` 等等. 435 | 436 | 如果我们把刚才的那个错误的文件夹名称改过来,从`my_modules`改成`node_modules`,那么搜索过程就会变成这样: 437 | 438 | ![mod-diagram-02](mod-diagram-02.png) 439 | 440 | 为了测试node到底加载了哪个模块,可以执行`require.resolve('some_module')` 命令,这会告诉你哪个文件路径下的模块被node找到并调用了。`require.resolve` 非常有用,尤其是在确认你*认为*被夹在的模块是*实际上*被加载的模块的时候--有时候一个不同版本的模块可能被存在了被更先查找的位置,导致你的代码调用了错误版本的模块。 441 | 442 | ### 如何写一个模块 443 | 444 | 现在你已经知道了如何找一个模块了,在这之后你就可以开始开发自己的模块了! 445 | 446 | #### The simplest possible module 447 | 448 | Node的模块十分的轻量化。这里有一个最简单的node模块: 449 | 450 | `package.json`: 451 | 452 | ```js 453 | { 454 | "name": "number-one", 455 | "version": "1.0.0" 456 | } 457 | ``` 458 | 459 | `index.js`: 460 | 461 | ```js 462 | module.exports = 1 463 | ``` 464 | 465 | 默认情况下,当你调用`require('module')`时node会试图加载`module/index.js`,除非你在`package.json`中设定了`main`一项内容指向你的代码,不然用的名称的文件无法被node识别。 466 | 467 | 把这两个文件放到`number-one`目录下(`package.json`中的`id`一项必须和目录的名称相同),然后你就可以加载他们了。 468 | 469 | 调用`require('number-one')` 这一命令会返回你在模块中`module.exports`输出的内容: 470 | 471 | ![simple-module](simple-module.png) 472 | 473 | 一个更快捷的创建模块的方法是,执行以下命令: 474 | 475 | ```sh 476 | mkdir my_module 477 | cd my_module 478 | git init 479 | git remote add git@github.com:yourusername/my_module.git 480 | npm init 481 | ``` 482 | 执行`npm init`会生成一个`package.json`,如果你是在一个`git`项目里执行,它还会在`package.json`中自动帮你把`repositories`设成你的git repo地址! 483 | 484 | #### 添加依赖项 485 | 486 | 一个模块可以添加其它在npm上或是在Github上的模块到他的配置文件`package.json`中的`dependencies`项。如果你想安装一个新的依赖项,并把它自动添加到`package.json`中,在你的模块的根目录中执行这个命令: 487 | 488 | ```sh 489 | npm install --save request 490 | ``` 491 | 这个命令会安装`request`模块到最近的`node_modules`文件夹中,并会把`package.json`改成这样: 492 | 493 | ``` 494 | { 495 | "id": "number-one", 496 | "version": "1.0.0", 497 | "dependencies": { 498 | "request": "~2.22.0" 499 | } 500 | } 501 | ``` 502 | 默认情况下 `npm install`会安装模块的最新版本。 503 | 504 | ## 用npm在客户端开发 505 | 506 | 人们对npm有一个常见的错误观念,认为npm的名字中有一个Node,所以只能用于服务器端的JS模块。一派胡言!npm的全称是Node Packaged Modules,是由node为你打包过的模块。而模块本身可以是任何东西--本质上只是一个被打包成.tar.gz的文件夹,和一个声明了模块版本和模块依赖项的配置文件`package.json` (也包括依赖项的版本,这样对应版本的依赖项会被自动安装)。这是无穷无尽的--模块可以有依赖,模块的依赖项也可以有依赖,依赖项的依赖项也可以有依赖。。。 507 | 508 | [browserify](http://browserify.org/) 是一个用Node写的实用工具,可以讲任何node模块转换成可以在浏览器上运行的代码。当然,并不是所有模块都能工作(比如浏览器无法搭一个HTTP服务器),但是很多NPM上的模块*可以*。 509 | 510 | 你可以用[RequireBin](http://requirebin.com/)来尝试在浏览器上使用npm的模块,这是一个[原作者](https://github.com/maxogden)写的应用,它在[Browserify-CDN](https://github.com/jesusabdullah/browserify-cdn)的基础上完成。原作在RequireBin中使用了browserify,并通过HTTP返回输出结果(而不是通过命令后--browserify通常都是用来干这个) 511 | 512 | 513 | 试着将下面的代码粘贴到[RequireBin](http://requirebin.com/)并点`preview`按钮: 514 | 515 | ```js 516 | var reverse = require('ascii-art-reverse') 517 | 518 | // makes a visible HTML console 519 | require('console-log').show(true) 520 | 521 | var coolbear = 522 | " ('-^-/') \n" + 523 | " `o__o' ] \n" + 524 | " (_Y_) _/ \n" + 525 | " _..`--'-.`, \n" + 526 | " (__)_,--(__) \n" + 527 | " 7: ; 1 \n" + 528 | " _/,`-.-' : \n" + 529 | " (_,)-~~(_,) \n" 530 | 531 | setInterval(function() { console.log(coolbear) }, 1000) 532 | 533 | setTimeout(function() { 534 | setInterval(function() { console.log(reverse(coolbear)) }, 1000) 535 | }, 500) 536 | ``` 537 | 538 | 或者看这个[更复杂的例子](http://requirebin.com/?gist=6031068)(可以随意改变它的颜色): 539 | 540 | [![requirebin](requirebin.png)](http://requirebin.com/embed?gist=6031068) 541 | 542 | ## 析薪杝矣 543 | ``` 544 | 原文的标题是Going with the Grain,大意是顺应着木材的纹理(刨木),不违背它 545 | 此处的'析薪杝矣'出自詩·小雅: 546 | 伐木掎矣,析薪杝矣 547 | 大意为,砍伐树木时,要撑住使大树不致突然倒下;劈木材,要依循木材的纹理,才比较容易 548 | 549 | ``` 550 | 551 | 像任意一个顺手的工具一样,node非常强大,但也只适用于特定的应用场景。比如,Rails这个网络架构,非常适合做一些复杂的[框架]((http://en.wikipedia.org/wiki/Business_logic)),比如用代码来构建生活中的业务对象:帐户、借贷、流程图、存货清单等等。虽然从技术上讲,用node可以完成同样的工作,但这并不是node的强项,node更适合去做一些处理I/O问题的工作。希望这个教程能够帮你获得对node适用方案的直觉。 552 | 553 | ### node外的世界 554 | 555 | node只是一个处理文件系统和网络I/O的工具,它把更多有趣的功能留给第三方模块来处理。以下是node核心模块之外奇妙世界的一些介绍: 556 | 557 | #### 网络框架 558 | 559 | 有许多搭建在node之上的网络框架(框架是一种解决特定高层应用问题的功能集合),但是node自身并不是一个网络框架。一些搭建在node之上的网络框架有自己的特性、抽象和权衡,这些和node自身的理念与开发优先级不一定相同。 560 | 561 | #### 编程语法 562 | 563 | Node适用Javascript的语法并且没有加以修饰。 Felix Geisendörfer针对node的风格有一篇很棒的[介绍](https://github.com/felixge/node-style-guide)。 564 | 565 | #### 语言的抽象 566 | 567 | node用最简单的方式来完成任务。在Javascirpt中,你想把它做的越有趣,就会带来更大的复杂度。编程是有难度的,尤其是在写js的时候更有这种体会,因为你应对的每一个问题都可能有1000种解决方案。正是因为如此,node试图用最简单、通用的方式来解决问题。如果你在处理一个很复杂的问题,并且你并不满意node应用的‘vanilla JS’解决方案,你大可不用它,并且自己写一个模块,用你自己喜欢的方法来解决它。 568 | 569 | 一个很棒的例子就是node中的回调函数。 早期node的一些实验中,有一个特性叫做‘promises’。它被用来使异步运行的代码看上去更线性。但是出于以下原因,这个特性后来被移除了: 570 | 571 | - 它比回调函数更复杂 572 | - 它可以让用户来选择应用(在npm上以第三方模块的形式发布) 573 | 574 | 试着考虑node处理的最基本最通用的事情:读取一个文件,当你读一个文件的时候,你希望在诸如硬盘错误这种事件发生的时候能及时知道。如果node用了上述的’promises‘特性,那么每个人的代码就会变成这样: 575 | 576 | ```js 577 | fs.readFile('movie.mp4') 578 | .then(function(data) { 579 | // do stuff with data 580 | }) 581 | .error(function(error) { 582 | // handle error 583 | }) 584 | ``` 585 | 586 | 这添加了复杂度,而且并不是所有人都想要这个特性。 node会用一个简单的回调函数来完成这两个独立的功能。其它的诸如此的规则还有: 587 | 588 | - 当没有错误的时候,对第一个参数返回null 589 | - 当有错误的时候,对第一个参数返回错误代码 590 | - 其它的变量可以用来做任何事情(node多数情况下在读写东西,所以这些变量通常被用来传数据或响应) 591 | 592 | 基于上述规则写出来的回调函数则应是这样的: 593 | 594 | ```js 595 | fs.readFile('movie.mp4', function(err, data) { 596 | // handle error, do stuff with data 597 | }) 598 | ``` 599 | 600 | #### 线程/纤程/非事件的并发处理 601 | 注意:如果你并不知道这些词的含义,你可能会学Node学的更轻松一些。 602 | 603 | Node内部使用线程来加速操作,但是这些部分并不会暴露给用户。如果你是专业人员,并且对node的设计理念十分好奇的话,推荐你阅读这篇[the design of libuv](http://nikhilm.github.com/uvbook/),这个是node使用的C++ I/O层。 604 | 605 | ## 使用许可 606 | 607 | ![CCBY](CCBY.png) 608 | 609 | 原文适用知识共享许可协议 610 | http://creativecommons.org/licenses/by/2.0/ 611 | 612 | 捐款图标来源于 [Noun Project](http://thenounproject.com/term/donate/285/) 613 | -------------------------------------------------------------------------------- /readme.zh-tw.md: -------------------------------------------------------------------------------- 1 | # Node 的藝術 2 | ## Node.js 入門 3 | 4 | 本文件假設讀者已經懂了以下的兩樣東西: 5 | 6 | - 至少懂得一種程式語言。例如:JavaScript,Ruby,Python,Perl 或其他程式語言。如果你還不是程式設計師,你也不懂程式語言,你可以閱讀 [JavaScript for Cats](http://jsforcats.com/)。:cat2: 7 | - git 和 github。這是一個開源的協作工具,Node 社群的參與者使用 git 共享模組。你只需要懂得基本操作就夠了。這裏有三篇很好的入門教學:[1](https://github.com/jlord/git-it-electron#readme), [2](http://zachbruggeman.me/github-for-cats/), [3](http://opensourcerer.diy.org/) 8 | 9 | This short book is a work in progress + I don't have a job right now (if I did I wouldn't have the time to write this). If you like it then please consider donating via [gittip](https://www.gittip.com/maxogden/) so that I can write more! 10 | 11 | > 譯者: 上面這段我沒有翻譯,因爲我希望保持原文。上面作者提到,目前他還沒找到工作。如果你喜歡這份文件,希望你可以通過 [gittip](https://www.gittip.com/maxogden/) 樂捐給作者。這樣作者才能夠寫更多內容。 12 | 13 | [![donate](donate.png)](https://www.gittip.com/maxogden/) 14 | 15 | ## 目錄 16 | 17 | - [瞭解Node](#node-1) 18 | - [核心模組](#-1) 19 | - [回呼機制](#callbacks) 20 | - [Events](#events) (not written yet) 21 | - [Streams](#streams) (not written yet) 22 | - [Modules and NPM](#modules) (not written yet) 23 | - [Going with the grain](#going-with-the-grain) 24 | - [Real-time apps](#realtime) (not written yet) 25 | 26 | ## 瞭解Node 27 | 28 | Node.js 是一個自由軟體專案,目的是讓你通過編寫 JavaScript 的程式進行網路、檔案系統或者與其他輸入/輸出裝置溝通的程式。就這樣!它只是一個簡單而穩定的輸入/輸出平臺,你可以在這個平臺上建構模組。 29 | 30 | 那有沒有一些關於輸入/輸出的實際例子? 這裏有張我用 Node.js 製作的應用程式結構圖,你可以看到上面有很多輸入/輸出裝置: 31 | 32 | ![server diagram](server-diagram.png) 33 | 34 | 如果你無法完全瞭解上圖顯示的所有東西,那沒關係。重點是你看到一個 Node 的運作(中間的六邊形那個),它就像經紀人,管理全部輸入/輸出的節點(橙色和紫色的線條代表輸入/輸出)。 35 | 36 | 一般上我們撰寫的程式可以分爲以下兩類: 37 | 38 | - 很難寫,但是效率超高(就像用 C 從零開始編寫一個網頁伺服器) 39 | - 很好寫,但是不夠效率/強大(就像有人試圖上傳 5GB 的檔案去你伺服器,但是伺服器卻當掉了) 40 | 41 | Node 試圖在這兩者之間做到平衡:容易理解與使用,並且在多數的情況下能夠快速開發。 42 | 43 | Node 不是以下兩樣東西: 44 | 45 | - 不是Web框架 (不像 Rails 或 Django,儘管它可以被用來做這樣的事情) 46 | - 不是程式語言(Node 使用 JavaScript 程式語言,它沒有自己的語言) 47 | 48 | 相反,Node 是: 49 | 50 | - 設計上簡單,而且容易明白和使用的平臺 51 | - 適合那些需要快速和處理很多輸入/輸出連接的程式 52 | 53 | 在底層,Node 可以作爲一種工具,並編寫出以下兩類程式: 54 | 55 | - 需要使用到Web協議(如:HTTP、TCP、UDP、DNS和SSL)的網路程式 56 | - 需要對檔案系統或者本機執行緒/記憶體進行寫入或讀出操作的程式 57 | 58 | 什麼是“輸入/輸出程式”? 這裏有一些常見的輸入/輸出裝置: 59 | 60 | - 資料庫 (如:MySQL、PostgreSQL、MongoDB、Redis、CouchDB) 61 | - APIs(如:Twitter、Facebook、Apple Push Notifications) 62 | - HTTP/WebSocket的連線(從使用者的Web應用程式) 63 | - 文件檔(圖像縮放軟件、影音編輯器、網路收音機) 64 | 65 | Node能 夠[非同步處理](http://en.wikipedia.org/wiki/Asynchronous_I/O)多個不同種類的輸入/輸出來源。比如說,假設你來到快餐店,你向店員要了一個起士堡,他們會馬上爲你下單和準備漢堡。然後,他們會要求你在旁邊等漢堡完成。在你等待這段時間,他們可以接受其他訂單和幫其他人準備漢堡。試想一下,如果你站在櫃檯前面,一直等到你的起士堡完成,那麼你就阻礙了後面的人下訂單,廚師也不能幫其他人準備漢堡!我們稱這個爲**阻塞式 I/O**,因爲一次只能處理一個 I/O 操作(廚師一次只能準備一個漢堡)。Node 並不是這樣的,它是**非阻塞**的,也就是說它能一次準備很多漢堡。 66 | 67 | 感謝 Node 的非阻塞特性,讓我們可以實現以下這些有趣事情: 68 | 69 | - 控制 [Quadcopters 飛行](http://nodecopter.com) 70 | - 編寫 IRC 談天機器人 71 | - 製作一個[雙腳走路的機器人](http://www.youtube.com/watch?v=jf-cEB3U2UQ) 72 | 73 | ## 核心模組 74 | 75 | 首先,你需要安裝 Node 到你的電腦。Node 安裝很簡單,只需瀏覽 [nodejs.org](http://nodejs.org) 和按下 `Install`. 76 | 77 | Node 擁有一組核心模組(通常被稱爲`Node 核心`)提供公用 API 讓你開發時呼叫。我們可以呼叫 `fs` 模組來操作檔案系統。當我們要進行網路操作時候,我們會呼叫網路模組,例如:`net`(TCP),`http`,`dgram`(UDP)。 78 | 79 | 除了 `fs` 和網路模組之外,Node核心還有很多其他的核心模組。如 `dns` 模組用來非同步解析DNS查詢。`os` 模組可以用來收集作業系統的資訊,如 tempdir 的路徑。`buffer` 模組可以處理二進制資料。還有些模組可以處理 URL 和路徑,如:`url`,`querystring` 和 `path` 等等。大部分的核心模組都支援 Node 的主要使用目標:快速編寫能夠進行檔案或網路操作的程式。 80 | 81 | Node 通過回呼機制,事件,串流和模組來控制 I/O。如果你知道這四樣東西是如何工作的,那麼你就能夠靈活使用任何核心模組,並且懂得如何與這些模組串聯。 82 | 83 | ## 回呼機制 84 | 85 | 如果你想瞭解如何使用 Node,這將會是最重要的課題。幾乎在 Node 中的所有事情都會使用到回呼機制。這並不是 Node 發明的,它只是一種呼叫 JavaScript 函式的特殊方式。 86 | 87 | 回呼函式是指非同步,或者在將來某個時間才會被執行的函式。同步程式執行的順序是從上到下,而非同步的程式卻是在不同的時間執行不同的函式,這些都基於早些執行函式的順序和時間,像是 HTTP 請求和從文件系統裡讀取內容等等。 88 | 89 | 這種同步和非同步之間的差異可能會讓人感到困惑,因為解讀一個函式是不是非同步,很大的程度上取決於具體的情況。下面是一個簡單的同步函式例子: 90 | 91 | ```js 92 | var myNumber = 1 93 | function addOne() { myNumber++ } // 定義函式 94 | addOne() // 執行函式 95 | console.log(myNumber) // 輸出 2 96 | ``` 97 | 98 | 上面的程式碼定義了一個函式,然後呼叫該函式,之間沒有任何停留。當該函式被呼叫時,它立即把那個數字加上 1,所以我們可以預見到,呼叫過該函式後,那個數字的值會變成 2。 99 | 100 | 現在假設我們把數字保存在一個叫 `numbr.txt` 的文件裡: 101 | 102 | ```js 103 | var fs = require('fs') // require 是 Node 裡提供的一個特別函式 104 | var myNumber = undefined // 數字裡保存在文件裡,因此我們並不知道它的值 105 | 106 | function addOne() { 107 | fs.readFile('./number.txt', function doneReading(err, fileContents) { 108 | myNumber = parseInt(fileContents) 109 | myNumber++ 110 | }) 111 | } 112 | 113 | addOne() 114 | 115 | console.log(myNumber) // 輸出 undefined 116 | ``` 117 | 118 | 為什麼當我們輸出值的時候是顯示 `undefined` ? 因為在上面的程式碼中,我們使用了 `fs.readFile` 這個方法, 而它恰好是個非同步方法。 一般來說,需要和硬碟或網路通信的,都是非同步的。如果只需要從記憶體或 CPU 裡讀取的話,這就是同步的。這是因為 I/O(輸入/輸出)是非常非常非常慢的。如果要大概形容一下,從硬碟裡讀取大概比從記憶體(RAM)裡讀取慢了 10 萬倍。 119 | 120 | 當這個程式執行的時候,所有的函式都馬上被定義,但它們不是馬上都被執行的。這是撰寫非同步程式時的一個基礎概念。當 `addOne` 被呼叫的時候,Node 執行 `readFile` 這個方法,但不等到 `readFile` 結束,它會繼續執行下一個準備好的函式。如果沒有可以執行的函式,Node 要麼會停下來,等待文件讀取或是網路通信結束,要麼就退出程式。 121 | 122 | 當 `readFile` 把文件讀取完成(需要的時間從幾毫秒到幾秒到幾分鐘不等,要看硬碟有多快),Node 會執行 `doneReading` 這個函數,並把錯誤(如果讀取文件出現錯誤)和文件的內容傳給它。 123 | 124 | 在上面程式中,之所以會顯示 `undefined` 是因為我們的程式碼在輸出數字之前,並沒有在任何地方告訴 `console.log` 等待 `readFile` 結束。 125 | 126 | 如果你有一些想要反複執行的程式碼,你應該做的第一件就是把這些程式碼放在一個函數裡。然後,在你需要執行的時候,呼叫這個函數就行了。 127 | 128 | 回呼函式,只是一個在將來某個時間點會被執行的函式。要理解回呼函式,關鍵的一點是它被使用的時機。你使用回呼函式的前題是,你不知道**什麼時候**某個非同步操作會完成,但知道這個操作會在**哪裡**結束————就在那個非同步函式的最後一行!你在什麼地方宣告這些函式並不重要,重要的是這些函式之間的羅輯/階層。把程式碼拆成各個函式之後,如果一個函式的执行取決於另一個函式何時結束,就該使用回呼函式了。 129 | 130 | 上面程式碼中 `fs.readFile` 方法是 Node 自帶的,這個方法是非同步的,而且要花費很長時間。想想看它要做多少事情:它要進入操作系统,進入文件系统,文件系统可是在硬碟上的,硬碟可能轉得飛快,也可能根本就不轉。然後它要用激光讀出資料,並把資料傳回你的 JavaScript 程式。當你給了它一個回呼函式後,它就可以在成功地從文件系統中取得資料以後,呼叫那個回呼函式。它會把資料放在一個變數裡,傳入你給的回呼函式,我們給這個參數起的名字叫做 `fileContents`,因為參數中包含的是讀取到的文件内容。 131 | 132 | 想想看本文剛開始的那個餐廳例子。在很多餐廳,你點的菜上來之前,服務生會放一個數字牌在你桌上。這個和回呼函式很類似。回呼函式的作用就是告訴服務員在你的起士漢堡好了後要做些什麼。 133 | 134 | 現在,讓我們把 `console.log` 放進一個函式裡作回呼函式使用吧。 135 | 136 | ```js 137 | var fs = require('fs') 138 | var myNumber = undefined 139 | 140 | function addOne(callback) { 141 | fs.readFile('./number.txt', function doneReading(err, fileContents) { 142 | myNumber = parseInt(fileContents) 143 | myNumber++ 144 | callback() 145 | } 146 | } 147 | 148 | function logMyNumber() { 149 | console.log(myNumber) 150 | } 151 | 152 | addOne(logMyNumber) 153 | ``` 154 | 现在 `logMyNumber` 這個函式可以被傳給 `addOne` 作為回呼函式了。在 `readFile` 完成後,`callback` 這個變數會被執行(也就是 `callback()`)。只有函式才能被執行,所以如果你提供一個不是函式的東西,程式會出錯。 155 | 156 | 在 JavaScript 裡,當函式被呼叫,其包含的程式碼會立刻被執行。在這個例子裡,`console.log` 會被執行,因為 `callback` 其實就是 `logMyNumber`。要記得,你*定義*了一个函式,不代表它會執行!你一定得*呼叫*它才行。 157 | 158 | 如果要更仔細地分析一下這個例子,下面是按時間順序排列的所有發生的事件: 159 | - 1: 程式碼被分析,此時,如果有任何語法錯誤,程式會中斷並報錯。 160 | 2: `addOne` 被呼叫,以 `logMyName` 作為它的回呼函式,也就是我們想在 `addOne` 結束後執行的函式。接下來,非同步的 `fs.readFile` 馬上開始執行。這個部分要花上點時間。 161 | - 3: 目前 Node 没事做,於是它就閒下來等待 `readFile` 結束。 162 | 4: `readFile` 結束了,`doneReading` 這個函式被呼叫,它把數字加上 1 然後馬上呼叫回呼函式————也就是我們傳給 `addOne` 的 `logMyNumber`。 163 | 164 | 也許關於回呼函式最難理解的部份是,為什麼函式可以存在變數裡被傳來傳去,而且還有著變來變去的名字。要讓你的程式碼更容易被看懂,給你的函式取簡單明瞭的名字是很重要的。總的來說,在使用 Node 時,如果你看見一個變數叫做 `callback` 或是它的縮寫 `cb`,你差不多可以確定它就是一個函式。 165 | 166 | 你可能聽過一個術語叫“事件驅動程式設計”,或者叫“事件循環”。`readFile` 這類的函式就利用了“事件循環”。Node 首先開始始執行 `readFile`,並等待著 `readFile` 傳回一個事件。在 Node 等待的這段時間,它可以繼續執行其他的程式碼。在 Node 裡有一個列表,裡面記下了所有開始執行卻還沒有傳回結束訊號的事,Node 就一遍遍循環檢查這個列表,看看有沒有事情完成了。它們執行完之後,就會被指定成處理完,接著執行依賴的回呼函式。 167 | 168 | 下面是上面的虛擬碼版本: 169 | 170 | ```js 171 | function addOne(thenRunThisFunction) { 172 | waitAMinuteAsync(function waitedAMinute() { 173 | thenRunThisFunction() 174 | }) 175 | } 176 | 177 | addOne(function thisGetsRunAfterAddOneFinishes() {}) 178 | ``` 179 | 180 | 試想你有三個非同步函式:`a`、`b`,和 `c`。它們執行時間都要花上一分鐘,執行完後會呼叫一个回呼函式(以第一個参數的形式被傳進函式)。如果你想讓 Node 先執行 `a`,`a` 執行完後執行 `b`,`b` 執行完後再執行 `c`,那麼程式碼可以寫成下面這樣: 181 | 182 | ```js 183 | a(function() { 184 | b(function() { 185 | c() 186 | }) 187 | }) 188 | ``` 189 | 190 | 當這段程式碼被執行時,`a` 馬上就會被執行,一分鐘後 `a` 結束,`b` 開始執行,再一分鐘後,`b` 結束,`c` 開始執行。最後,也就是三分鐘後,Node 會終止,因為所有事都執行完畢。上面的程式碼可能看起來沒那麼漂亮,但重點是,如果有些程式碼需要在某些非同步的事情完成之後再執行,你需要做的是把那些程式碼放進一個函式,當作回呼函式傳給非同步函式,以表示回呼函式中的程式碼要依賴非同步的部份結束後才能執行。 191 | 192 | Node 要求你用非線性的思維思考。看看下面這兩件事: 193 | 194 | ``` 195 | read a file 196 | process that file 197 | ``` 198 | 如果你只是不假思索地把這兩件事改成虛擬碼,你會這麼寫: 199 | 200 | ``` 201 | var file = readFile() 202 | processFile(file) 203 | ``` 204 | 205 | 這種線性的程式碼不是 Node 的風格。(線性是指一步接一步、按照順序地執行)。如果上面的程式碼被執行了。那麼 `readFile` 和 `processFile` 會同時被呼叫。這根本說不通,因為 `reafFile` 要花上一陣子才能完成執行。正確的做法是,表達清楚 `processFile` 是要依賴 `readFile` 結束才能運行的。這就是回呼函式的功用了!因為 JavaScript 的特點,有好幾種方法可以表達這種依賴性: 206 | 207 | ```js 208 | var fs = require('fs') 209 | fs.readFile('movie.mp4', finishedReading) 210 | 211 | function finishedReading(error, movieData) { 212 | if (error) return console.error(error) 213 | // do something with the movieData 214 | } 215 | ``` 216 | 217 | 不過你這樣寫也可以,照樣能成功執行: 218 | 219 | ```js 220 | var fs = require('fs') 221 | 222 | function finishedReading(error, movieData) { 223 | if (error) return console.error(error) 224 | // do something with the movieData 225 | } 226 | 227 | fs.readFile('movie.mp4', finishedReading) 228 | ``` 229 | 230 | 甚至像下面這樣: 231 | 232 | ```js 233 | var fs = require('fs') 234 | 235 | fs.readFile('movie.mp4', function finishedReading(error, movieData) { 236 | if (error) return console.error(error) 237 | // do something with the movieData 238 | }) 239 | ``` 240 | 241 | ## Events 242 | 243 | In node if you require the [events](http://nodejs.org/api/events.html) module you can use the so-called 'event emitter' that node itself uses for all of its APIs that emit things. 244 | 245 | Events are a common pattern in programming, known more widely as the ['observer pattern'](http://en.wikipedia.org/wiki/Observer_pattern) or 'pub/sub' (publish/subscribe). Whereas callbacks are a one-to-one relationship between the thing waiting for the callback and the thing calling the callback, events are the same exact pattern except with a many-to-many API. 246 | 247 | Here are few common use cases for using events instead of plain callbacks: 248 | 249 | - Chat room where you want to broadcast messages to many listeners 250 | - Game server that needs to know when new players connect, disconnect, move, shoot and jump 251 | - Database connector that might need to know when the database connection opens, closes or sends an error 252 | 253 | If we were trying to write a module that connects to a chat server using only callbacks it would look like this: 254 | 255 | ```js 256 | var chatClient = require('my-chat-client') 257 | 258 | function onConnect() { 259 | // have the UI show we are connected 260 | } 261 | 262 | function onConnectionError(error) { 263 | // show error to the user 264 | } 265 | 266 | function onDisconnect() { 267 | // tell user that they have been disconnected 268 | } 269 | 270 | function onMessage(message) { 271 | // show the chat room message in the UI 272 | } 273 | 274 | chatClient.connect( 275 | 'http://mychatserver.com', 276 | onConnect, 277 | onConnectionError, 278 | onDisconnect, 279 | onMessage 280 | ) 281 | ``` 282 | 283 | As you can see this is really cumbersome because of all of the functions that you have to pass in a specific order to the `.connect` function. Writing this with events would look like this: 284 | 285 | ```js 286 | var chatClient = require('my-chat-client').connect() 287 | 288 | chatClient.on('connect', function() { 289 | // have the UI show we are connected 290 | }) 291 | 292 | chatClient.on('connectionError', function() { 293 | // show error to the user 294 | }) 295 | 296 | chatClient.on('disconnect', function() { 297 | // tell user that they have been disconnected 298 | }) 299 | 300 | chatClient.on('message', function() { 301 | // show the chat room message in the UI 302 | }) 303 | ``` 304 | 305 | This approach is similar to the pure-callback approach but introduces the `.on` method, which subscribes a callback to an event. This means you can choose which events you want to subscribe to from the `chatClient`. You can also subscribe to the same event multiple times with different callbacks: 306 | 307 | ```js 308 | var chatClient = require('my-chat-client').connect() 309 | chatClient.on('message', logMessage) 310 | chatClient.on('message', storeMessage) 311 | 312 | function logMessage(message) { 313 | console.log(message) 314 | } 315 | 316 | function storeMessage(message) { 317 | myDatabase.save(message) 318 | } 319 | ``` 320 | 321 | MORE EVENTS CONTENT TODO 322 | 323 | ## Streams 324 | 325 | Early on in the project the file system and network APIs had their own separate patterns for dealing with streaming I/O. For example, files in a file system have things called 'file descriptors' so the `fs` module had to have extra logic to keep track of these things whereas the network modules didn't have such a concept. Despite minor differences in semantics like these, at a fundamental level both groups of code were duplicating a lot of functionality when it came to reading data in and out. The team working on node realized that it would be confusing to have to learn two sets of semantics to essentially do the same thing so they made a new API called the `Stream` and made all the network and file system code use it. 326 | 327 | The whole point of node is to make it easy to deal with file systems and networks so it made sense to have one pattern that was used everywhere. The good news is that most of the patterns like these (there are only a few anyway) have been figured out at this point and it is very unlikely that node will change that much in the future. 328 | 329 | THE REST IS TODO, in the meantime read the [streams handbook](https://github.com/substack/stream-handbook#introduction) 330 | 331 | ## Modules 332 | 333 | TODO 334 | 335 | ## Going with the grain 336 | 337 | Like any good tool, node is best suited for a certain set of use cases. For example: Rails, the popular web framework, is great for modeling complex [business logic](http://en.wikipedia.org/wiki/Business_logic), e.g. using code to represent real life business objects like accounts, loan, itineraries, and inventories. While it is technically possible to do the same type of thing using node, there would be definite drawbacks since node is designed for solving I/O problems and it doesn't know much about 'business logic'. Each tool focuses on different problems. Hopefully this guide will help you gain an intuitive understanding of the strengths of node so that you know when it can be useful to you. 338 | 339 | ### What is outside of node's scope? 340 | 341 | Fundamentally node is just a tool used for managing I/O across file systems and networks, and it leaves other more fancy functionality up to third party modules. Here are some things that are outside the scope of node: 342 | 343 | #### Web frameworks 344 | 345 | There are a number of web frameworks built on top of node (framework meaning a bundle of solutions that attempts to address some high level problem like modeling business logic), but node is not a web framework. Web frameworks that are written using node don't always make the same kind of decisions about adding complexity, abstractions and tradeoffs that node does and may have other priorities. 346 | 347 | #### Language syntax 348 | 349 | Node uses JavaScript and doesn't change anything about it. Felix Geisendörfer has a pretty good write-up of the 'node style' [here](https://github.com/felixge/node-style-guide). 350 | 351 | #### Language abstraction 352 | 353 | When possible node will use the simplest possible way of accomplishing something. The 'fancier' you make your JavaScript the more complexity and tradeoffs you introduce. Programming is hard, especially in JS where there are 1000 solutions to every problem! It is for this reason that node tries to always pick the simplest, most universal option. If you are solving a problem that calls for a complex solution and you are unsatisfied with the 'vanilla JS solutions' that node implements, you are free to solve it inside your app or module using whichever abstractions you prefer. 354 | 355 | A great example of this is node's use of callbacks. Early on node experimented with a feature called 'promises' that added a number of features to make async code appear more linear. It was taken out of node core for a few reasons: 356 | 357 | - they are more complex than callbacks 358 | - they can be implemented in userland (distributed on npm as third party modules) 359 | 360 | Consider one of the most universal and basic things that node does: reading a file. When you read a file you want to know when errors happen, like when your hard drive dies in the middle of your read. If node had promises everyone would have to branch their code like this: 361 | 362 | ```js 363 | fs.readFile('movie.mp4') 364 | .then(function(data) { 365 | // do stuff with data 366 | }) 367 | .error(function(error) { 368 | // handle error 369 | }) 370 | ``` 371 | 372 | This adds complexity, and not everyone wants that. Instead of two separate functions node just uses a single callback function. Here are the rules: 373 | 374 | - When there is no error pass null as the first argument 375 | - When there is an error, pass it as the first argument 376 | - The rest of the arguments can be used for anything (usually data or responses since most stuff in node is reading or writing things) 377 | 378 | Hence, the node callback style: 379 | 380 | ```js 381 | fs.readFile('movie.mp4', function(err, data) { 382 | // handle error, do stuff with data 383 | }) 384 | ``` 385 | 386 | #### Threads/fibers/non-event-based concurrency solutions 387 | 388 | Note: If you don't know what these things mean then you will likely have an easier time learning node, since unlearning things is just as much work as learning things. 389 | 390 | Node uses threads internally to make things fast but doesn't expose them to the user. If you are a technical user wondering why node is designed this way then you should 100% read about [the design of libuv](http://nikhilm.github.com/uvbook/), the C++ I/O layer that node is built on top of. 391 | 392 | ## Real-time apps 393 | 394 | TODO - this section will have a non-contrived, functioning application with a web UI whose architecture will be dissected and discussed. 395 | 396 | ## License 397 | 398 | ![CCBY](CCBY.png) 399 | 400 | Creative Commons Attribution License (do whatever, just attribute me) 401 | http://creativecommons.org/licenses/by/2.0/ 402 | 403 | Donate icon is from the [http://thenounproject.com/noun/donate/#icon-No285](Noun Project) 404 | -------------------------------------------------------------------------------- /requirebin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/requirebin.png -------------------------------------------------------------------------------- /server-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/server-diagram.png -------------------------------------------------------------------------------- /simple-module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/simple-module.png -------------------------------------------------------------------------------- /stream-adventure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/stream-adventure.png -------------------------------------------------------------------------------- /stream-handbook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/max-mapper/art-of-node/71d0aba060532c5b4f075c1cd0fad746515f3808/stream-handbook.png --------------------------------------------------------------------------------