第三章 从阻塞顺序性编程风格到事件驱动型和异步编程风格

一 异步与阻塞,事件驱动与顺序执行

1.什么是异步,什么是事件驱动,异步有什么好处,有什么坏处

A君和B君今天都是计划去银行办点事,然后去超市买点日用品.他们都来到银行,A去自动提款机那里开始排队,前面大概有20来人,她只能依次排队等,取到钱后她再去超市.B去到排队机抽了个号码,他一看前面还有很多人,预计要比较长事件,然后去隔壁超市找要买的东西,听到银行广播自己号码时候,再回来银行办事.

对于A君,我们叫同步,程序只能按预定的顺序执行,遇到耗时长的操作时候,需要等待其完成才能执行下一步任务.对于A君,他有个好处就是按部就班,不怕出现意外情况,例如身上钱不够这些问题.但明显会消耗更多时间.

对于B君,我们叫异步,程序遇到需要等待长的操作时候,不等待其完成而马上执行下一步任务,再等到长时间操作完成后再回头去继续处理后续问题.这样好处是节约时间,但如果你事情多的话,这样就变得有些乱,不好管理.

而银行的广播会触发B君回去银行,这个我们叫事件驱动.当然这样不好的地方很多,例如出错处理会更加麻烦,事情会变得混乱不好整理.

2 如何阅读node的代码

由于事件驱动的存在,我们不能像其他的一些代码那样顺序查看,我们应该先大概预览一次代码,然后以"事件"为关键词,搜索代码,找到代码的开头入口.

例如一个简单web程序:

var http = require(‘http‘);

var url = require(‘url‘);

var Web = function(req,res){

this.req = req;
    this.res = res;

};

Web.prototype[‘/hello‘] = function(){
    this.res.end("hello world");
}

Web.prototype[‘/404‘] = function(){
    this.res.
    this.res.end("404 not found");
}

http.createServer(function(req, res){

var reqinfo = url.parse(req.url);
 var web = new Web(req, res);
 var path = reqinfo.pathname.toLowerCase();
 if (path && web[path]){
    try{
        web[path]();
    }catch(err){

throw err;
   }

}else{
     web[‘/404‘];

}

}).listen(80);

你可以发现,如果你从头开始看,它是不会符合事情的顺序来的,我们应该从http.createServer这里开始入手.而段代码却是在程序的最后面.
所以看node的代码,你要做的第一件事不仔细看每个代码,而是快速浏览一次,并且找到正确的入口.

(PS:最开始,我作为PHP程序员进入我的前公司,但措手不及的0基础接手了一个离职的研究所写node半成品(半成品都没,因为程序运行不了)项目,我用了PHP的习惯来查看代码,寻找代码的思路,导致了我一周完全摸不着头脑.这种痛苦至今刻骨铭心,也感谢这种刻骨铭心,让我学其他各种语言更加得心应手)

二 回调与避免深度嵌套

1.深度嵌套的典型例子

如下面的这个读取文件内容的函数:

fs.readFile(‘/etc/passwd‘, function (err, data) {
  if (err) throw err;
  console.log(data);
});

那,我们读取两个文件,将这两个文件的内容合并到一起处理怎么办呢?大多数接触js不久的人可能会这么干:

fs.readFile(‘/etc/passwd‘, function (err, data) {
  if (err) throw err;
  fs.readFile(‘/etc/passwd2‘, function (err, data2) {
    if (err) throw err;
    // 在这里处理data和data2的数据
  });
});

那要是处理多个类似的场景,岂不是回调函数一层层的嵌套?

我们常常把这个问题叫做”回调黑洞”或”回调金字塔”:

doAsync1(function () {
  doAsync2(function () {
    doAsync3(function () {
      doAsync4(function () {
    // .... doAsyncN(..)
    })
  })
})

这种层层嵌套的代码给开发带来了很多问题,主要体现在:

1.代码可读性变差(非常严重)
2.调试困难
3.出现异常后难以排查
4.改写困难.

回调黑洞是一种主观的叫法,就像嵌套太多的代码,有时候也没什么问题。为了控制调用顺序,异步代码变得非常复杂,这就是黑洞。有个问题非常合适衡量黑洞到底有多深:如果doAsync2发生在doAsync1之前,你要忍受多少重构的痛苦?目标不单单是减少嵌套层数,而是要编写模块化(可测试)的代码,便于理解和修改。

2.node v0.1.1版本后的解决方法

2.1 Generator

Generator是为JavaScript设计的一种轻量级的协程。它通过yield关键字,可以控制一个函数暂停或者继续执行,  本身generator的引入并不是用来解决异步问题的,但yield和next接口恰巧可以为我们解决深层回调的问题,让我们接受异步产生的结果,继续完成接下来的任务,与回调的目的一致。
     generator实际上是一种迭代器。它很像一个可以返回数组的函数,有参数,可以调用,并且会生成一系列的值。然而generator不是把数组中的值都准备好然后一次性返回,而是一次yield一个,所以它所需的资源更少,并且调用者可以马上开始处理开头的几个值。简言之,generator看起来像函数,但行为表现像迭代器。这感觉代码中使用function*来表示generator很有相似.

2.2generator使用, yied,next,function*和co库.

(如果你熟悉go语言或者python 你肯定对这个东西不陌生.)
Node对generator的支持是从v0.11.2开始的,但因为还没正式发布,所以需要指明--harmony或--harmony-generator参数启用它(NodeJS使用V8引擎,而V8引擎对ES6中的东西有部分支持,所以在NodeJS中可以使用一些ES6中的东西。但是由于很多东西只是草案而已,也许正式版会删除,所以还没有直接引入。而是把他们放在了和谐(harmony)模式下,如果你需要用,需要指明)。
例如运行文件test.js 我们需要

#node --harmony test.js

#forever start -c "node --harmony" test.js //如果使用forever启动,这也是forever支持ES6的方法

首先定义一个简单的generator,并使用它:

function* Foo(x) {   yield x + 1;  var y = yield null;
  console.log(y);  return x + y;
}

var foo = Foo(5);
foo.next(); // { value: 6, done: false }
foo.next(); // { value: null, done: false }
foo.next(8); // { value: 13, done: true }

generator的定义跟普通函数差不多,只是在 function 关键字后面多了一个 *号。而调用generator后会返回一个generator对象,其中保存了generator的内部执行状态。每调用一次generator的next方法,就会得到一个包含执行结果的对象,含有两个域 value 和 done 。 value 是此次执行generator的返回值, done 为generator是否已经执行完的标志。如果对 done 为 true 的generator对象调用next方法则会抛出Error: Generator has already finished 错误。

generator中使用了一个新的关键字 yield ,它的作用与 return 差不多,除了可以从generator中返回值给外部使用外,还可以暂停该generator的执行,也可以通过next传递值给generator的上一次中断位置(因此y得到8)并从上次中断位置继续执行代码。

我们关注next的代码

var foo = Foo(5); //创建对象,代码不执行
foo.next(); // { value: 6, done: false } next传入了undefined,执行了 x+1 返回 x+1的结果
foo.next(); // { value: null, done: false } next 传入了undefined,执行了 null,返回 null的结果
foo.next(8); // { value: 13, done: true }传入了8 并被y接收 执行了console.log(y)和return x+ y;

待续

三 循环与递归循环

四 错误处理

五 约定的编程风格

时间: 2024-10-10 09:23:35

第三章 从阻塞顺序性编程风格到事件驱动型和异步编程风格的相关文章

C#编程总结(六)异步编程

C#编程总结(六)异步编程 1.什么是异步? 异步操作通常用于执行完成时间可能较长的任务,如打开大文件.连接远程计算机或查询数据库.异步操作在主应用程序线程以外的线程中执行.应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行. 2.同步与异步的区别 同步(Synchronous):在执行某个操作时,应用程序必须等待该操作执行完成后才能继续执行. 异步(Asynchronous):在执行某个操作时,应用程序可在异步操作执行时继续执行.实质:异步操作,启动了新的线程,主线程

Java编程的逻辑 (94) - 组合式异步编程

前面两节讨论了Java 8中的函数式数据处理,那是对38节到55节介绍的容器类的增强,它可以将对集合数据的多个操作以流水线的方式组合在一起.本节继续讨论Java 8的新功能,主要是一个新的类CompletableFuture,它是对65节到82节介绍的并发编程的增强,它可以方便地将多个有一定依赖关系的异步任务以流水线的方式组合在一起,大大简化多异步任务的开发. 之前介绍了那么多并发编程的内容,还有什么问题不能解决?CompletableFuture到底能解决什么问题?与之前介绍的内容有什么关系?

多线程之异步编程: 经典和最新的异步编程模型,async与await

经典的异步编程模型(IAsyncResult) 最新的异步编程模型(async 和 await) 将 IAsyncInfo 转换成 Task 将 Task 转换成 IAsyncInfo 示例1.使用经典的异步编程模型(IAsyncResult)实现一个支持异步操作的类Thread/Async/ClassicAsync.cs /* * 使用经典的异步编程模型(IAsyncResult)实现一个支持异步操作的类 */ using System; using System.Collections.Ge

第三章 一个简单的机器学习例子让你了解DeepLab的语言风格

MINST是由Yann LeCun等人建立并维护的手写数字识别数据库.该数据库总共包含60000个训练样本和10000个测试样本.其中每个样本的大小是一张28*28的手写数字图片.数字包括从0~9总共10个数字.遵照上述条件,使用如下语句建立softmax分类模型:代码清单3.1.1 layer1 = SoftmaxReg;%建立单层softmax神经网络 layer1.inputsize = 28*28; %设置输入层大小 layer1.numClasses = 10;%设置分类数 layer

一个例子读懂 JS 异步编程: Callback / Promise / Generator / Async

JS异步编程实践理解 回顾JS异步编程方法的发展,主要有以下几种方式: Callback Promise Generator Async 需求 显示购物车商品列表的页面,用户可以勾选想要删除商品(单选或多选),点击确认删除按钮后,将已勾选的商品清除购物车,页面显示剩余商品. 为了便于本文内容阐述,假设后端没有提供一个批量删除商品的接口,所以对用户选择的商品列表,需要逐个调用删除接口. 用一个定时器代表一次接口请求.那思路就是遍历存放用户已选择商品的id数组,逐个发起删除请求del,待全部删除完成

深入解析Javascript异步编程

这里深入探讨下Javascript的异步编程技术.(P.S. 本文较长,请准备好瓜子可乐 :D) 一. Javascript异步编程简介 至少在语言级别上,Javascript是单线程的,因此异步编程对其尤为重要. 拿nodejs来说,外壳是一层js语言,这是用户操作的层面,在这个层次上它是单线程运行的,也就是说我们不能像Java.Python这类语言在语言级别使用多线程能力.取而代之的是,nodejs编程中大量使用了异步编程技术,这是为了高效使用硬件,同时也可以不造成同步阻塞.不过nodejs

<史上最强>深入理解 Python 异步编程(上)

前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知道如何使用 Tornado.Twisted.Gevent 这类异步框架上,出现各种古怪的问题难以解决.而且使用了异步框架的部分同学,由于用法不对,感觉它并没牛逼到哪里去,所以很多同学做 Web 后端服务时还是采用 Flask.Django等传统的非异步框架. 从上两届 PyCon 技术大会看来,异步编程已经成了 Python 生态下一阶段的主旋律.如新兴的 Go.Rust.

利用python yielding创建协程将异步编程同步化

转自:http://www.jackyshen.com/2015/05/21/async-operations-in-form-of-sync-programming-with-python-yielding/ 目录 回顾同步与异步编程 回顾多线程编程 yield与协程 异步编程同步化 回顾同步与异步编程 同步编程即线性化编程,代码按照既定顺序执行,上一条语句执行完才会执行下一条,否则就一直等在那里.但是许多实际操作都是CPU 密集型任务和 IO 密集型任务,比如网络请求,此时不能让这些任务阻塞

深入理解 Python 异步编程(上)

http://python.jobbole.com/88291/ 前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知道如何使用 Tornado.Twisted.Gevent 这类异步框架上,出现各种古怪的问题难以解决.而且使用了异步框架的部分同学,由于用法不对,感觉它并没牛逼到哪里去,所以很多同学做 Web 后端服务时还是采用 Flask.Django等传统的非异步框架. 从上两届 PyCon 技术大会看来,异步编程已经成