原文 Node.js Tutorial: Promise, Generator, Event and Filestreamhtml
在前面的教程中,你已经看到了咱们在异步事件中使用回调函数。可是有些时候当它们开始不断嵌套,而且程序变得愈来愈长愈来愈复杂的时候,回调函数就会像恶梦通常。node
在这些状况下,Node.js 提供了额外的特性来修复咱们在使用回调的时候遇到的问题。这些特性被分红 Promise, generates 和 events. 这篇文章咱们会更详细地学习和理解这些概念。git
在咱们开始讲 promises 以前,让咱们首先来回顾一下 Node.js 中 "callback" 是什么。咱们在前面的章节中看到过不少,因此让咱们快速地过一下其中的一个。github
下面的例子展现了一段代码片断,它被用来链接一个 MongoDB 的数据库,而且在数据库的一条记录中执行更新操做。数据库
代码解释:npm
上面的代码中,function(err,db) 这段代码被称做匿名函数的声明或者回调函数。当 MongoClient 建立了一个 MongoDB 数据库的链接时,一旦这个链接操做结束,它就会返回到回调函数执行。因此,某种意义上来讲,链接操做在后台运行,而且当它结束以后,就会调用这个回调函数。记住这是之因此 Node.js 可让不少操做同时发生可是不会阻塞用户执行操做的关键。promise
第二段代码片断就是回调函数被调用的时候真正执行的代码。这个回调函数就在 MongoDB 数据库中更新一条记录。异步
那么什么是 promise 呢?promise 就是 Node.js 中回调函数的一个改进。在开发的时候,咱们可能遇到这样的场景,咱们可能须要将多个回调函数嵌套起来。这种状况在一段时间后会变得有些杂乱和难以维护。简单的说,promise 就是来解决回调的这些问题的一个改进版。函数
promise 的基本语法就像下面这样:工具
var promise = doSomethingAsync(); promise.then(onFullfilled, onRejected);
doSomethingAsync
指的是任何执行某些异步操做回调或者匿名函数
这一次,当定义回调的时候,有一个叫作promise
的值返回给咱们
当一个 promise 返回的时候,它有两个输出。这个由then clause
来定义。这个操做要么成功,这里经过onFullfilled
参数来表示,要么出错,这里经过onRejected
参数来表示。
注意: 因此 promise 的关键是这个返回值。当咱们在 Node.js 中处理正常的回调的时候,并无返回值的概念。由于有了这个返回值,咱们对回调函数有了更多的控制权。
如今让咱们看一个例子,看看咱们在 Node.js 应用中是如何使用 promises
的。为了可以在 Node.js 的应用程序中使用 promise
,这个 promise
模块首先须要下载和安装。
接下来咱们用 promises 来修改咱们的代码,来更新 Employee
集合中的 Employeename。
安装 npm 模块
为了可以在 NodeJS 应用中使用 promise, 咱们须要安装这个 promise 模块,运行下面的命令
npm install promise
修改代码来引入 promise
代码解释:
第一部分是包含这个promise
模块,让咱们可以在代码中可使用 promise 的功能。
如今咱们能够在 MongoClient.connect
函数后面添加then
函数啦。因此,这段代码的意思是当创建数据库链接以后,咱们须要执行定义在then
里面的代码片断。
最后,咱们定义了执行更新操做的代码片断
注意:
若是你如今检查 MongoDB 数据库,你会发现若是Martin
雇员的名字存在,它会被更新成Mohan
.
若是要检查是否在数据库中正确插入,你须要执行下面 MongoDB 的命令。
use EmployeeDB
db.Employee.find({EmployeeName:mohan})
第一个命令是保证你连上 EmployeeDB
的数据库。第二个命令是查找名字为 Mohan
的雇员的纪录
当咱们定义 promises 的时候,须要注意的是then
方法自己返回的也是一个 promise。因此,某种意义上来讲 promises 能够嵌套或者链式调用。
下面的例子中,咱们使用链式调用来定义了两个回调函数。两个都向数据库插入了一条记录。
注意:链式调用是把一个方法的执行连接到另外一个上的概念。假如你的应用有两个方法分别叫作methodA
和methodB
.而且逻辑是methodB
要在methodA
以后调用,这个时候你就能够把这种执行顺序经过在methodA
以后直接调用methodB
来连接起来。
这个例子须要注意的关键是经过使用嵌套 promises 使得代码变得更简洁,可读和可维护了。
代码解释:
咱们定义了两个then
语句来实如今一个执行完以后再执行另外一个。第一个then
语句,咱们将包含数据库链接的db
做为参数传递给它。接下来咱们经过使用db
链接的属性collection
来插入一条记录。这个insertOne
方法被用来插入一个真正的文档对象给Employee
集合。
接下来咱们使用第二个then
语句来向数据库插入另外一条记录。
若是你如今查看 MongoDB 数据库,你会在数据库中找到这两条记录。
Bluebird 是一个功能完善的 promise 库。Bluebird 最重要的特性是它可以 promisify
其余Node
模块来异步地使用它们。Promisify 是一个应用在回调函数上的概念。这个概念用来保证每个回调函数会返回一些值。
因此,若是一个 NodeJS 模块包含了一个不返回任何值的回调函数,若是咱们 Promisify
这个node
模块,那么全部在这个模块内部的函数都会被自动修改保证它有返回值。
因此,你可使用 Bluebird 让 MongoDB 模块异步运行。这样就从另外一层面上提升了写 Node.js 应用程序的温馨性。
咱们接下来就看一个若是使用 bluebird 模块的例子。
咱们的例子首先创建一个到 EmployeeDB
数据库中 Employee collection
的一个链接。若是接下来链接创建好了,它就会获取全部的集合中的记录而且在控制台中打印出来。
安装 npm 模块
要在 Node 应用程序中使用 Bluebird,必须包含这个模块,运行下面的命令:
npm install bluebird
接下来就是包含这个 bluebird 模块,而后 promise 化整个 MongoDB 模块。这里的 promise 化,咱们的意思是 bluebird 确保每个定义在 MongoDB 类库中的方法都会返回一个 promise。
代码解释:
require
命令是用来包含这个 Bluebird 库的。
经过使用 Bluebird 的 promisifyAll()
这个方法来建立 MongoDB 模块中每个方法的异步版本。这确保每个 MongoDB 模块的方法都会在后台中运行,而且确保 MongoDB 库的每个方法都会返回一个 promise。
最后一步就是链接数据库,获取集合中的全部记录而后在控制台展现它们。
代码解释:
你会发现咱们这里使用connectAsync
方法而不是正常的connect
方法来链接数据库。Bluebird 是经过给每个方法添加 Async 关键字来区分返回 promise 和不返回 promise 的调用。因此咱们并不能保证没有 Async 关键字的方法会有返回值。
和connectAsync
相似,咱们如今使用findAsync
方法来返回全部在 MongoDB 数据库中 Employee
集合中的记录。
最后,若是这个 findAsync
方法成功返回一个 promise ,咱们接下来就能够定义一段代码来遍历集合中的每个记录而后把它们的内容打印出来。
若是上面的步骤正确的执行,全部在Employee
集合的文档都会被打印到控制台,就像下面这样.
一个自定义promise能够经过一个叫作q
的 node 模块来建立。这个q
模块须要经过 node 包管理工具来下载和安装。在使用q
这个库以后,这个denodeify
方法被调用以后会让任何函数变成一个会返回 promise 的函数。
下面的例子中,咱们首先建立一个简单的函数叫作Add
,它会把两个数加起来。咱们要让这个函数转化为一个返回 promise 对象的函数.
一旦转化成功以后,咱们就会经过这个Add
函数返回的 promise 来在控制台中展现信息
接下来让咱们按照下面的方法来建立一个自定义的返回 promise 对象的方法。
安装 npm 模块
要使用q
这个模块,咱们须要在 NodeJs 应用程序中包含它。安装这个q
模块,咱们能够运行下面的命令
npm install q
定义下面的代码来建立自定义的 promise
代码解释:
第一段的做用是经过require
关键字包含这个q
库,经过使用这个库,咱们就可以定义任何函数来返回一个回调函数。
咱们建立了一个叫作 Add 的函数,它会把两个有变量a
和b
表示的数字加起来,结果保存在c
中。
咱们使用q
库来denodeify
(用来将任何函数转化成可以返回 promise 的函数的方法)咱们的Add
函数。
如今咱们调用这个Add
函数就可以返回一个 promise ,由于咱们以前denodeify
了这个Add
函数
这里的then
关键词被用来表示当函数成功执行以后在控制台中展现Addition function completed
.
当上面的代码执行以后,就会在控制台输出下面的内容
generators 最近在 Node.js 中变得至关有名,这也许和它们可以作的事情有关。
Generators 是一个可以让函数执行挂起,而且在后面的某个时间点恢复
Generators 在实现好比说懒执行
的概念的时候很是有用。这基本意味着经过挂起操做和为所欲为地恢复执行,咱们可以只有在咱们须要的时候取数据。
Generators 有下面两个关键字:
Yield 方法:这个yield
方法是在要在中止函数执行的时候调用的,函数就在yield
方法被调用的那一行中止。
Next 方法:这个next
方法从主应用调用去恢复拥有yield
方法的函数的执行。这个函数会一直执行到遇到下一个yield
方法或者直到函数的结束.
让咱们看一下下面的例子看看 generators 是怎么使用的。
下面的例子中,咱们会有一个把两个数字相加的Add
函数,可是可是咱们会在函数中不一样的地方中止函数的执行来展现 generators 是如何使用的。
代码解释:
第一步是定义咱们的 generator 函数。注意这里是经过在 function 关键字后面添加*
完成的。接下来咱们定义一个叫作Add
的函数,接受一个x
做为参数.
这个yield
关键字只用在 generators 中。这让它拥有可以在函数的人和位置暂停的能力。因此在这里,这个函数会一直中止,知道咱们调用next()
函数,在第4步会调用。在这里,x
的值会变成6而且函数的执行会中止。
这里就是咱们调用 generator 函数而且把 5 做为参数传给这个函数的地方。
一旦咱们调用next()
方法,Add()
函数会恢复执行。当咱们执行到下面这一个语句var y = yield(null)
的时候又会中止执行。(此时返回的 value 是 6,而不是上面说的 null)
再次调用next()
方法,一只执行到return x + y;
结束。(此时返回的 value 是 null,而不是上面说的 11)。
再次调用next()
方法,会直接返回 11.
Generators 被用来解决一个叫作回调hell的问题。有些时候在 Node.js 应用中,回调函数嵌套的如此之深以致于它变得太复杂而不能使用回调。
这个时候 Generators 就派上用场啦。一个最经常使用的例子就是建立定时器函数。
让咱们看看下面的例子来看看 generators 是如何证实它比回调更有用的。
咱们的例子只建立一个简单的时间延迟函数。咱们接下来会在 1s,2s,3s以后调用这个函数。
1, 建立一个回调函数有必要的时间延迟的代码
代码解释:
这里咱们建立了一个叫作Timedelay
的函数,里面包含一个叫作 ptime 的参数,这个可让咱们等待给定的时间来执行回调
下一步就是建立一条消息,告诉用户说这个应用程序会暂停这么多时间
2, 如今咱们来看一下若是咱们包含不少回调的代码。假如咱们想要在每次调用以后都增长 1s 再调用回调函数,下面的代码就是咱们须要的用来实现的回调代码。
代码解释:
咱们以 1000 做为参数调用 Timedelay 函数
接下来,咱们以 2000 做为参数调用 Timedelay 函数
最后,咱们以 3000 做为参数调用 Timedelay 函数
3, 如今咱们来看看如何经过 generators 来实现上面同样的功能。