├── demo ├── SUMMARY.md ├── package.json ├── book.js └── README.md ├── toc2.gif ├── preview.png ├── book ├── toc2.css └── toc2.js ├── index.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /demo/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Introduction](README.md) 4 | 5 | -------------------------------------------------------------------------------- /toc2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i5ting/gitbook-plugin-toc2/HEAD/toc2.gif -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i5ting/gitbook-plugin-toc2/HEAD/preview.png -------------------------------------------------------------------------------- /book/toc2.css: -------------------------------------------------------------------------------- 1 | .toc2 { 2 | background: white; 3 | max-width: 240px; 4 | position: fixed; 5 | right: 0px; 6 | bottom: 0px; 7 | } 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var toc = require('marked-toc'); 2 | 3 | module.exports = { 4 | website: { 5 | assets: "./book", 6 | js: [ 7 | "toc2.js" 8 | ], 9 | css: [ 10 | "toc2.css" 11 | ] 12 | }, 13 | book: { 14 | }, 15 | hooks: { 16 | "page:before": function(page) { 17 | var tmpl = '<%= depth %><%= bullet %>[<%= heading %>](#<%= url %>)\n'; 18 | // console.log(tmpl) 19 | page.content = toc.insert(page.content, {template: tmpl}); 20 | // console.log(page.content) 21 | return page; 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "17koa", 3 | "version": "1.0.0", 4 | "description": "![Cover](cover.png)", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "watch -i 300ms gitbook serve", 8 | "open": "npm run build && open _book/index.html", 9 | "build": "gitbook build . --gitbook=2.0.1", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git@git.oschina.net:i5ting/book.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "dependencies": { 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo/book.js: -------------------------------------------------------------------------------- 1 | var pkg = require('./package.json'); 2 | 3 | module.exports = { 4 | // Documentation for GitBook is stored under "docs" 5 | root: './', 6 | title: 'GitBook Toolchain Documentation', 7 | 8 | // Enforce use of GitBook v3 9 | gitbook: '>=3.0.0-pre.0', 10 | 11 | // Use the "official" theme 12 | plugins: ["toc2"], 13 | 14 | variables: { 15 | version: pkg.version 16 | }, 17 | 18 | pluginsConfig: { 19 | sitemap: { 20 | hostname: 'https://toolchain.gitbook.com' 21 | }, 22 | "toc": { 23 | "addClass": true, 24 | "className": "toc" 25 | } 26 | } 27 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | demo/_book/ 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | node_modules 29 | 30 | # Optional npm cache directory 31 | .npm 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitbook-plugin-toc2", 3 | "version": "1.0.5", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "marked-toc": "^0.3.0" 8 | }, 9 | "engines": { 10 | "gitbook": "*" 11 | }, 12 | "keywords": [ 13 | "gitbook", 14 | "plugin" 15 | ], 16 | "devDependencies": {}, 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/i5ting/gitbook-plugin-toc2.git" 23 | }, 24 | "author": "", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/i5ting/gitbook-plugin-toc2/issues" 28 | }, 29 | "homepage": "https://github.com/i5ting/gitbook-plugin-toc2#readme" 30 | } 31 | -------------------------------------------------------------------------------- /book/toc2.js: -------------------------------------------------------------------------------- 1 | require(["gitbook", "jQuery"], function(gitbook, $) { 2 | 3 | // Return true if sidebar is open 4 | function isOpen() { 5 | return gitbook.state.$book.hasClass('with-summary'); 6 | } 7 | 8 | var i = 1; 9 | $( window ).keydown(function( event ) { 10 | console.log(event.which) 11 | // enter = 13 12 | // t = 84 13 | if ( event.which === 13 || event.which === 84) { 14 | if (i % 2 == 1) { 15 | $('.markdown-section > ul').first().show().addClass('toc2') 16 | } else { 17 | $('.markdown-section > ul').first().hide() 18 | } 19 | 20 | i++; 21 | } 22 | 23 | // h = hide 24 | if(event.which === 72){ 25 | $('.markdown-section > ul').first().hide() 26 | } 27 | }); 28 | 29 | 30 | gitbook.events.bind("page.change", function() { 31 | 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 alfred sang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitbook-plugin-toc2 2 | 3 | 在gitbook里,回车,显示或隐藏toc 4 | 5 | ![](toc2.gif) 6 | 7 | ## 用法 8 | 9 | 10 | 在book.js或book.json 11 | 12 | ``` 13 | { 14 | "plugins": ["toc2"], 15 | "pluginsConfig": { 16 | "toc2": { 17 | "addClass": true, 18 | "className": "toc" 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | 然后 25 | 26 | ``` 27 | $ gitbook install 28 | ``` 29 | 30 | 31 | 在markdown文件加入 ` `,当编译的时候回自动在这个位置增加toc 32 | 33 | 打开页面,默认会显示toc,回车,显示toc,再回车,隐藏toc。当然你也可以直接h隐藏 34 | 35 | ### 已知问题 36 | 37 | ``` 38 | - Callbacks:错误优先(error-first)的回调风格 39 | - EventEmitter:事件驱动 40 | 41 | 42 | ``` 43 | 44 | 这样会显示上面的ul作为toc,是不正确的,所以保证toc上面无ul 45 | 46 | 47 | 下面这种是ok的 48 | 49 | ``` 50 | 51 | 52 | - Callbacks:错误优先(error-first)的回调风格 53 | - EventEmitter:事件驱动 54 | ``` 55 | 56 | ## 关于gitbook插件 57 | 58 | ### 文档 59 | 60 | [官方的文档](http://developer.gitbook.com/plugins/index.html)极其的烂。。。。,这里还是推荐直接看源码里的文档 61 | 62 | https://github.com/GitbookIO/gitbook/tree/master/docs 63 | 64 | ### 插件原理 65 | 66 | 读取book.js或book.json 67 | 68 | ``` 69 | plugins: ["toc2"], 70 | ``` 71 | 72 | ### 插件配置 73 | 74 | ``` 75 | pluginsConfig: { 76 | sitemap: { 77 | hostname: 'https://toolchain.gitbook.com' 78 | }, 79 | "toc2": { 80 | "addClass": true, 81 | "className": "toc" 82 | } 83 | } 84 | ``` 85 | 86 | 87 | 用的时候 88 | 89 | ``` 90 | var pluginConfig = this.config.get('pluginsConfig.toc2', {}); 91 | ``` 92 | 93 | 和jq里的$.extend一样 94 | 95 | 96 | ### 入口index.js 97 | 98 | ``` 99 | module.exports = { 100 | // Extend website resources and html 101 | website: { 102 | assets: "./book", 103 | js: [ 104 | "test.js" 105 | ], 106 | css: [ 107 | "test.css" 108 | ], 109 | html: { 110 | "html:start": function() { 111 | return "" 112 | }, 113 | "html:end": function() { 114 | return "" 115 | }, 116 | 117 | "head:start": "", 118 | "head:end": "", 119 | 120 | "body:start": "", 121 | "body:end": "" 122 | } 123 | }, 124 | 125 | // Extend ebook resources and html 126 | website: { 127 | assets: "./book", 128 | js: [ 129 | "test.js" 130 | ], 131 | css: [ 132 | "test.css" 133 | ], 134 | html: { 135 | "html:start": function() { 136 | return "" 137 | }, 138 | "html:end": function() { 139 | return "" 140 | }, 141 | 142 | "head:start": "", 143 | "head:end": "", 144 | 145 | "body:start": "", 146 | "body:end": "" 147 | } 148 | }, 149 | 150 | // Extend templating blocks 151 | blocks: { 152 | // Author will be able to write "{% myTag %}World{% endMyTag %}" 153 | myTag: { 154 | process: function(blk) { 155 | return "Hello "+blk.body; 156 | } 157 | } 158 | }, 159 | 160 | // Extend templating filters 161 | filters: { 162 | // Author will be able to write "{{ 'test'|myFilter }}" 163 | myFilter: function(s) { 164 | return "Hello "+s; 165 | } 166 | }, 167 | 168 | // Hook process during build 169 | hooks: { 170 | // For all the hooks, this represent the current generator 171 | 172 | // This is called before the book is generated 173 | "init": function() { 174 | console.log("init!"); 175 | }, 176 | 177 | // This is called after the book generation 178 | "finish": function() { 179 | console.log("finish!"); 180 | } 181 | } 182 | ``` 183 | 184 | 按需就好,一般的css、js和回调hook等比较常用 185 | 186 | ### 入口指定的js 187 | 188 | 为了防止大量插件影响速度,所以才有requirejs这种amd的模块化加载机制 189 | 190 | ``` 191 | require(["gitbook", "jQuery"], function(gitbook, $) { 192 | 193 | // Return true if sidebar is open 194 | function isOpen() { 195 | return gitbook.state.$book.hasClass('with-summary'); 196 | } 197 | 198 | var i = 1; 199 | $( window ).keydown(function( event ) { 200 | console.log(event.which) 201 | // enter = 13 202 | // t = 84 203 | if ( event.which === 13 || event.which === 84) { 204 | if (i % 2 == 1) { 205 | $('.markdown-section > ul').first().show().addClass('toc2') 206 | } else { 207 | $('.markdown-section > ul').first().hide() 208 | } 209 | 210 | i++; 211 | } 212 | 213 | // h = hide 214 | if(event.which === 72){ 215 | $('.markdown-section > ul').first().hide() 216 | } 217 | }); 218 | 219 | 220 | gitbook.events.bind("page.change", function() { 221 | 222 | }); 223 | 224 | }); 225 | 226 | ``` 227 | 228 | 内置jquery,所以整体来说和普通的jquery插件没啥区别,难度比较小 229 | 230 | ### 结合node模块写扩展 231 | 232 | 比如本例子中 233 | 234 | ``` 235 | var toc = require('marked-toc'); 236 | ``` 237 | 238 | 所以它的package.json里就需要安装对应的依赖 239 | 240 | ``` 241 | "dependencies": { 242 | "marked-toc": "^0.3.0" 243 | }, 244 | ``` 245 | 246 | ### 事件 247 | 248 | - page:before 249 | - page:change 250 | - init 251 | 252 | 事件绑定 253 | 254 | ``` 255 | gitbook.events.bind("page.change", function() { 256 | 257 | }); 258 | ``` 259 | 260 | 等等 261 | 262 | 有时间比较有回调hook 263 | 264 | 其实如果为了高扩展,可以把hook写到配置项里,和jq插件一样 265 | 266 | ### engine配置 267 | 268 | package.json必须写 269 | 270 | ``` 271 | "engines": { 272 | "gitbook": "*" 273 | }, 274 | ``` 275 | 276 | 不然安装的时候找不到。即gitbook install的时候做的手脚 277 | 278 | ### 慢的问题 279 | 280 | 目前还无解 281 | 282 | 只能把SUMMARY里的内容减少到你当前需要的,切勿把所有的都放里,避免死翘翘 283 | 284 | ### npm scripts 285 | 286 | ``` 287 | "scripts": { 288 | "start": "watch -i 300ms gitbook serve", 289 | "open": "npm run build && open _book/index.html", 290 | "build": "gitbook build . --gitbook=2.0.1", 291 | "test": "echo \"Error: no test specified\" && exit 1" 292 | }, 293 | ``` 294 | 295 | 经常是一遍修改,然后服务器自动reload,这样有的时候会导致gitbook死掉,最好的办法是使用tj的watch监察一下。 296 | 297 | ## 总结 298 | 299 | gitbook的插件设计还是非常不错的。从markdown需要编译处下手,完成插件切入,集成。另外插件的配置方式、hook等都可圈可点。 300 | 301 | 通过gitbook install来安装gitbook-plugin-xx,也是个不错的实践。 302 | 303 | 对于设计express或koa插件机制来说,是个比较好的参考。 304 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | 4 | 5 | 6 | 7 | 8 | ## 什么异步和同步? 9 | 10 | 首先得了解什么是异步和同步的概念。 11 | 12 | 举个例子:比如你去cnode论坛发帖提问,多半是没有人立即回复的。这时可以采用两种做法。 13 | 14 | - 第一种做法:在网上一直等待,直到有人还书,然后再去吃饭睡觉。 15 | - 第二种做法:直接忙自己的去了,当有人回复的时候,它收到回复直接通知你。你则该忙什么忙什么。到时候等通知即可。 16 | 17 | 而第一种做法就是同步的表现,必须等待别人回复(等待服务器返回信息)才进行其他事情,至死方休。 18 | 19 | 而第二种做法就是异步的表现,不耽误时间,合理利用时间高效率做事(只要服务器能并行执行多少,就可以并行多少)。 20 | 21 | 遇到这种情况,你会采用那种办法呢? 22 | 23 | 选择第一种,哈哈,说明你太执着啦;选择第二种,说明你灵活变通,合理安排自己的人生。你自己看着办吧。 24 | 25 | ## Ajax中的异步 26 | 27 | > Ajax = Asynchronous JavaScript and XML 28 | 29 | 其中的`A = Asynchronous`,中文即异步的意思,相信很多人都了解这个,这里我们通过理解`Ajax`的原理来理解一下所谓的异步是什么 30 | 31 | 32 | ``` 33 | $.getJSON("/api/1",function(result){ 34 | // do something 35 | $("div").append("x"); 36 | }); 37 | console.log(1 + '\n') 38 | $.getJSON("/api/2",function(result){ 39 | // do something 40 | $("div").append("y"); 41 | }); 42 | console.log(2 + '\n') 43 | ``` 44 | 45 | 这里发了2个请求,2个$.getJSON是顺序执行的,所以会打出 46 | 47 | ``` 48 | 1 49 | 2 50 | ``` 51 | 52 | 至于x和y哪个先输出,要看具体服务器的响应时间,这部分就是异步操作。 53 | 54 | 55 | ## 异步原理 56 | 57 | 58 | `Node.js`异步原理因为`Event Loop`,我们调用node api方法的时候,它会把具体操作和callback丢给`Event Loop`去执行,`Event Loop`是一个回调函数队列,当异步函数执行时,回调函数会被压入这个队列。JavaScript引擎直到异步函数执行完成后,才会开始处理事件循环。这意味着JavaScript代码不是多线程的,即使表现的行为相似。事件循环是一个先进先出(FIFO)队列,这说明回调是按照它们被加入队列的顺序执行的。JavaScript被 `node` 选做为开发语言,就是因为写这样的代码多么简单啊。 59 | 60 | `Event Loop` 是 `libuv` 的核心实现,所以实际是 `libuv` 和 `system` 操作系统去打交道,此时会出现执行时长差异,所以也是异步的。 61 | 62 | 综上,无论`Ajax`还是`Node.js`,它们都是借助中间层去做实际操作,所以我们无需过多的关注中间层之后的操作就可以非常简单的完成功能开发,这其实就是它们最大的好处。 63 | 64 | ## Api 65 | 66 | > 应用程序接口(英语:Application Programming Interface,简称:API) 67 | 68 | 从`Node.js`异步原理,我们可以知道,核心在于api方法调用,然后交由`Event Loop`(libuv)去执行,所以我们一定要熟悉`Node.js`的api操作 69 | 70 | 举例: 获取目录下所有文件的api是fs.readdir 71 | 72 | ``` 73 | fs.readdir(path, callback)# 74 | Asynchronous readdir(3). Reads the contents of a directory. The callback gets two arguments (err, files) where files is an array of the names of the files in the directory excluding '.' and '..'. 75 | ``` 76 | 77 | 这里稍稍解释下 78 | 79 | - callback通常都是函数最后一个参数 80 | - callback里约定回调内容,(err, files) 81 | - err在前,没有err则为空 82 | - 具体返回结果在后 83 | 84 | 85 | ## 异步写法 86 | 87 | 使用回调函数,即为异步写法 88 | 89 | 见readdir-async.js 90 | 91 | ``` 92 | var fs = require('fs') 93 | 94 | var path = '.'; 95 | 96 | fs.readdir(path, function(err, files) { 97 | console.log(files) 98 | }) 99 | ``` 100 | 101 | ## 同步写法 102 | 103 | 同步写法即直接返回结果,不需要在回调函数里处理。 104 | 105 | 见readdir-sync.js 106 | 107 | ``` 108 | var fs = require('fs') 109 | 110 | console.log(fs.readdirSync('.')) 111 | ``` 112 | 113 | ## 执行并查看结果 114 | 115 | ``` 116 | node nodejs/async/readdir-sync.js 117 | [ '.DS_Store', 118 | '.git', 119 | '.gitignore', 120 | 'README.md', 121 | 'SUMMARY.md', 122 | '_book', 123 | 'async', 124 | 'cover.jpg', 125 | 'cover.png', 126 | 'cover_small.jpg', 127 | 'db', 128 | 'debug', 129 | 'docs', 130 | 'es-in-node4', 131 | 'framework', 132 | 'fullstack', 133 | 'getting-start', 134 | 'koa-core', 135 | 'koa-http', 136 | 'koa-in-action', 137 | 'koa.sketch', 138 | 'more', 139 | 'newbie', 140 | 'next', 141 | 'nodejs', 142 | 'npm', 143 | 'npm-debug.log', 144 | 'other', 145 | 'practice', 146 | 'referrence.md', 147 | 'wechat' ] 148 | ``` 149 | 150 | 从这个结果里我们可以看出,它的返回值是数组类型。我们如果想找出以koa开头的文件呢? 151 | 152 | ## 找出以koa开头的文件 153 | 154 | 核心是使用正则表达式来正则test方法的简单实例 155 | 156 | ``` 157 | files.forEach(function (file) { 158 | if (/^koa/.test(file)) { //正则 test 来查找以koa开头的文件 159 | console.log(file) 160 | } 161 | }) 162 | ``` 163 | 164 | 首先看一下异步写法,见readdir-async2.js 165 | 166 | ``` 167 | var fs = require('fs') 168 | 169 | var path = '.'; 170 | 171 | fs.readdir(path, function(err, files) { 172 | files.forEach(function (file) { 173 | if (/^koa/.test(file)) { 174 | console.log(file) 175 | } 176 | }) 177 | }) 178 | ``` 179 | 180 | 很明显,这是在回调函数里处理的,这是异步写法。 181 | 182 | 下面我们看一下同步写法 183 | 184 | 见readdir-sync2.js 185 | 186 | ``` 187 | var fs = require('fs') 188 | 189 | var files = fs.readdirSync('.') 190 | 191 | files.forEach(function (file) { 192 | if (/^koa/.test(file)) { 193 | console.log(file) 194 | } 195 | }) 196 | ``` 197 | 198 | 说明 199 | 200 | - 先获取files结果 201 | - 然后遍历files,进行处理 202 | 203 | 这里没有用回调函数,非常容易读懂,我们知道代码是顺序执行,所以这样写更容易让人理解 204 | 205 | ## 优化 206 | 207 | 这段代码写的还是比较冗余,我们来优化一下,把过滤处理的代码抽成一个独立的函数,能让我们代码具有更好的可读性,通过这个我们再比较一下同步异步的差别 208 | 209 | 先说异步,见readdir-async3.js 210 | 211 | ``` 212 | var fs = require('fs') 213 | 214 | var path = '.'; 215 | 216 | function filter(files){ 217 | files.forEach(function (file) { 218 | if (/^koa/.test(file)) { 219 | console.log(file) 220 | } 221 | }) 222 | } 223 | 224 | // 入口 225 | fs.readdir(path, function(err, files) { 226 | // 过滤 227 | filter(files) 228 | }) 229 | ``` 230 | 231 | 核心如下 232 | 233 | ``` 234 | // 入口 235 | fs.readdir(path, function(err, files) { 236 | // 过滤 237 | filter(files) 238 | }) 239 | ``` 240 | 241 | 这是一个比较好的理解异步的例子,代码看起来还不错。不过我们换个角度想想,如果我嵌套5层呢?是不是太可怕了。。。这不是好的写法,在后面流程控制章节里会具体讲 242 | 243 | 244 | 我们再来看看同步优化后的代码,见readdir-sync3.js 245 | 246 | ``` 247 | var fs = require('fs') 248 | 249 | function filter(files){ 250 | files.forEach(function (file) { 251 | if (/^koa/.test(file)) { 252 | console.log(file) 253 | } 254 | }) 255 | } 256 | 257 | var files = fs.readdirSync('.') 258 | 259 | filter(files) 260 | ``` 261 | 262 | 顺序执行,还是比较容易理解的。 263 | 264 | ## 极端情况 265 | 266 | 我们就以readdir-sync3.js为例, 267 | 268 | ``` 269 | var files = fs.readdirSync('.') 270 | // fs.readdirSync执行完成才会执行filter 271 | filter(files) 272 | ``` 273 | 274 | 这样的代码非常容易理解,我们再极端点,如果文件少效率是非常不错的,可是如果文件多呢?比较过亿个文件,程序执行就需要一定时间,这种同步代码会阻塞下面的方法执行,那是不是无法提高并行效率呢? 275 | 276 | 如果是异步呢? 277 | 278 | ``` 279 | // 入口 280 | fs.readdir(path, function(err, files1) { 281 | // 过滤 282 | filter(files1) 283 | }) 284 | 285 | fs.readdir(path, function(err, files2) { 286 | // 过滤 287 | filter(files2) 288 | }) 289 | ``` 290 | 291 | 这时,其实是执行2个`fs.readdir`方法的,它能够更好的利用系统资源完成更多的任务。 292 | 293 | ### 阻塞 294 | 295 | 模拟阻塞 296 | 297 | ``` 298 | var fs = require('fs') 299 | 300 | function filter(files){ 301 | files.forEach(function (file) { 302 | if (/^koa/.test(file)) { 303 | console.log(file) 304 | } 305 | }) 306 | } 307 | 308 | function sleep(milliSeconds) { 309 | var startTime = new Date().getTime(); 310 | while (new Date().getTime() < startTime + milliSeconds); 311 | }; 312 | 313 | var files = fs.readdirSync('.') 314 | 315 | sleep(10000); 316 | 317 | filter(files) 318 | 319 | 320 | ``` 321 | 322 | ### 并行任务 323 | 324 | async.js 325 | 326 | ``` 327 | var fs = require('fs') 328 | 329 | var path = '.'; 330 | 331 | 332 | // 入口 333 | fs.readdir(path, function(err, files) { 334 | // 过滤 335 | files.forEach(function (file) { 336 | if (/^koa/.test(file)) { 337 | console.log('1= ' + file) 338 | } 339 | }) 340 | }) 341 | 342 | 343 | fs.readdir(path, function(err, files) { 344 | // 过滤 345 | files.forEach(function (file) { 346 | if (/^koa/.test(file)) { 347 | console.log('2= ' + file) 348 | } 349 | }) 350 | }) 351 | ``` 352 | 353 | 多次执行,返回结果是不一定的,有的时候是1先执行,有的时候是2先执行,这其实就是因为这2个人是并行,快的先执行完 354 | 355 | ``` 356 | $ node nodejs/async/async.js 357 | 2= koa-core 358 | 2= koa-http 359 | 2= koa-in-action 360 | 2= koa.sketch 361 | 1= koa-core 362 | 1= koa-http 363 | 1= koa-in-action 364 | 1= koa.sketch 365 | $ node nodejs/async/async.js 366 | 1= koa-core 367 | 1= koa-http 368 | 1= koa-in-action 369 | 1= koa.sketch 370 | 2= koa-core 371 | 2= koa-http 372 | 2= koa-in-action 373 | 2= koa.sketch 374 | ``` 375 | 376 | ## 总结 377 | 378 | 本章节以`fs.readdir`为例,讲解了异步和同步的区别 379 | 380 | - 同步利于人的理解,但会造成线程阻塞,无法最大限度的利于系统资源 381 | - 异步写法需要嵌套回调,不利于理解和维护,哪怕代码写的非常规范也很难,但它能够并行,同时处理更多任务,效率会大于或等于异步 382 | 383 | 384 | --------------------------------------------------------------------------------