node.js 中的流程控制二

摘要: 编写上一篇介绍流程控制的文章给我带来了很大的乐趣,现在我想要处理一些反馈,另外还要讨论一下inimino所作的伟大工作。 当前node中有两种处理异步返回值的方法:promises和event emitters。关于两种方法的细节,你可以阅读nodejs.or...

编写上一篇介绍流程控制的文章给我带来了很大的乐趣,现在我想要处理一些反馈,另外还要讨论一下inimino所作的伟大工作。
当前node中有两种处理异步返回值的方法:promises和event emitters。关于两种方法的细节,你可以阅读nodejs.org上的介绍。我将会讨论这两种方法和另一种处理异步返回值和流事件(streaming events)的方法。

为什么要区分Promise和EventEmitter?

在node中有两种处理事件的类,它们是:Promise和EventEmitter。Promise是函数的异步表现形式。

  1. var File = require(file);
  2. var promise = File.read(mydata.txt);
  3. promise.addCallback(function (text) {
  4.   // Do something
  5. });
  6. promise.addErrback(function (err) {
  7.   // Handle error
  8. })

File.read接受文件名并返回文件内容。
有时我们需要监听可能多次发生的事件。例如,在一个web服务中,处理web请求时,body事件多次被触发,然后complete事件被触发。

  1. Http.createServer(function (reqres) {
  2.   var body = "";
  3.   req.addListener(bodyfunction (chunk) {
  4.     body += chunk;
  5.   });
  6.   req.addListener(completefunction () {
  7.     // Do something with body text
  8.   });
  9. }).listen(8080);

这两种方式的不同之处在于:在使用promise时,你会得到success事件或者error事件,但不会同时得到,也不会得到一个以上事件。在处理会发生多次的事件的时候,你就需要更强大的 EventEmitters。

创建自定义promise

假定我想为posix.open, posix.write, 和posix.close写一个便于使用的包装函数filewrite。(如下代码摘自”file”函数库中File.write函数的真实代码)

  1. function fileWrite (filenamedata) {
  2.   var promise = new events.Promise();
  3.   posix.open(filename"w"0666)
  4.     .addCallback(function (fd) {
  5.       function doWrite (_data) {
  6.         posix.write(fd_data0encoding)
  7.           .addCallback(function (written) {
  8.             if (written === _data.length) {
  9.               posix.close(fd);
  10.               promise.emitSuccess();
  11.             } else {
  12.               doWrite(_data.slice(written));
  13.             }
  14.           }).addErrback(function () {
  15.             promise.emitError();
  16.           });
  17.       }
  18.       doWrite(data);
  19.     })
  20.     .addErrback(function () {
  21.       promise.emitError();
  22.     });
  23.   return promise;
  24. };

filewrite函数可以以如下形式使用:

  1. fileWrite("MyBlog.txt""Hello World").addCallback(function () {
  2.   // It’s done
  3. });

请注意,我必须创建一个promise对象,执行操作,然后将结果传递给这个promise对象。

还有更好的方法

promises工作良好,但是继续读过inimino之后,它所使用的方法令我印象深刻。
是否还记得我们的第一个例子?假设我们按照如下方式使用File.read:

  1. var File = require(file);
  2. File.read(mydata.txt)(function (text) {
  3.   // Do something
  4. }function (error) {
  5.   // Handle error
  6. });

它不返回promise对象,而是返回一个接受两个回调函数作为参数的函数:一个处理成功,一个处理失败。我把这种风格成为“Do风格”,下面我详细解释:

编写回调风格的代码

如果我们想定义一个不立刻返回值的函数。使用”Do”风格,filewirte函数应当如下使用:(假定之前提到的posix函数也是这个风格)

  1. function fileWrite (filenamedata) { return function (callbackerrback) {
  2.   posix.open(filename"w"0666)(function (fd) {
  3.     function doWrite (_data) {
  4.       posix.write(fd_data0encoding)(
  5.         function (written) {
  6.           if (written === _data.length) {
  7.             posix.close(fd);
  8.             callback();
  9.           } else {
  10.             doWrite(_data.slice(written));
  11.           }
  12.         }errback);
  13.     }
  14.     doWrite(data);
  15.   }errback);
  16. }};

请注意,这样很容易就把错误信息返回给了调用者。同时,这种风格也使代码更短,更易阅读。
使用这种风格编写代码的关键是:不要返回promise,而是返回一个接受两个回调的函数,在需要的时候直接调用返回的函数。

“Do”函数库

前些日子我写了一个小型的函数库,叫做“Do”。实际上,它只有一个执行并行操作的函数,就像上一篇文章中介绍的“Combo”库。

实现

如下是整个函数的实现:

  1. Do = {
  2.   parallelfunction (fns) {
  3.     var results = [],
  4.         counter = fns.length;
  5.     return function(callbackerrback) {
  6.       fns.forEach(function (fni) {
  7.         fn(function (result) {
  8.           results[i] = result;
  9.           counter–;
  10.           if (counter <= 0) {
  11.             callback.apply(nullresults);
  12.           }
  13.         }errback);
  14.       });
  15.     }
  16.   }
  17. };

结合回调风格,使用这个函数可以写出非常强大和简介的代码。

执行单个操作

我们假定有一个实现了这个新技巧的函数readFIle,可以如下使用这个函数:

  1. // A single async action with error handling
  2. readFile(secretplans.txt)(function (secrets) {
  3.   // Do something
  4. }function (error) {
  5.   // Handle Error
  6. });

执行并行操作

我们继续使用”Do”函数库

  1. Do.parallel([
  2.     readFile('mylib.js'),
  3.     readFile('secretplans.txt'),
  4. ])(function (sourcesecrets) {
  5.   // Do something
  6. }function (error) {
  7.   // Handle Error
  8. });

上述代码代码并行执行了两个异步操作,并在全部执行完毕后执行指定代码。注意,如果没有错误发生,只有处理success的回调函数会被执行。如果出错,函数会将错误传递给通常的错误处理代码。
你也可传递一个文件名数组。

  1. var files = ["one.txt""two.txt""three.txt"];
  2. var actions = files.map(function (filename) {
  3.   return readFile(filename);
  4. });
  5.  
  6. Do.parallel(actions)(function () {
  7.   var contents = {},
  8.       args = arguments;
  9.   files.forEach(function (filenameindex) {
  10.     contents[filename] = args[index];
  11.   });
  12.   // Do something
  13. });
  14. // Let error thow exception.

执行顺序操作

要执行顺序操作,只需将函数“串起来”即可:

  1. readFile(names.txt)(
  2.   function upcase_slowly(string) { return function (next) {
  3.     setTimeout(function () {
  4.       next(string.toUpperCase());
  5.     }100);
  6.   }}
  7. )(
  8.   function save_data(string) { return function (next) {
  9.     writeFile(names_up.txtstring)(next);
  10.   }}
  11. )(function () {
  12.   // File was saved
  13. });

上述代码读取文件’names.txt’,完成之后调用upcase_slowly,然后将生成的新字符串专递给save_data函数。save_data函数是对writeFile的一个包装。当save_data函数执行完毕之后,将执行最终的回调函数。

Just for fun, here is the same example translated to the Jack language (still in development).
开个玩笑,如下代码是翻译成Jack语言(还在开发中)的示例代码:

  1. readFile names.txt
  2. fun string -> next ->
  3.   timeout 100fun ->
  4.     next string.toUpperCase()
  5. fun string -> next ->
  6.   writeFile names_up.txtstring | next
  7. fun ->
  8. File was saved

原文:http://howtonode.org/control-flow-part-ii,译者:lishen

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值