美文网首页
NodeJS——异步编程

NodeJS——异步编程

作者: Eylen | 来源:发表于2018-03-06 21:36 被阅读0次

1. 回调:通常用来处理一次性响应事件。(如指定一个回调函数来确定如何处理数据库查询结果)
2. 事件监听:通常用来响应重复性事件。本质上也是回调,只不过他是与一个概念实体(事件)相关联。(如HTTP服务器发出请求,进行请求监听,添加一些响应逻辑)。

回调

回调是一个函数,它作为参数传给异步函数,描述了异步操作完成后要做什么。

  • ex1:简单服务器
  1. 从titles.json文件中获取文章的标题
  2. 从template.html文件中获取HTML模板
  3. 将这些标题组装到HTML页面中
  4. 将HTML页面发送给用户

titles.json

{
  "Mike",
  "Jay",
  "Kim"
}

template.html

<html>
<head></head>
<body>
  <h1>Boys</h1>
  <ul>
     <li>%</li>
  </ul>
</body>
</html>

server1,js

var http = require("http");
var fs = require("fs");

http.createServer(function(req, res){
  if(req.url == '/'){
    fs.readFile('./titles.json', function(err, data){  //读取标题
      if(err)
      {
        console.log(err);
        res.end("server error");
      }else{
        var titles = JSON.parse(data.toString());  //得到标题的数组
        fs.readFile('./template.html', function(err, data) {  //读取模板
            if(err){
                console.log(err);
                res.end("server error");
            }else{
                var tmpl = data.toString();
                var html = tmpl.replace('%', titles.join('</li><li>'));  //组装标题
                res.writeHead(200, {'Content-type': 'text/html'});  //响应头部
                res.end(html);
            }
        })
      }
    });
  }
}).listen(8000, "127.0.0.1");

问题:三层嵌套

http.createServer(function(req, res){
  fs.readFile('./titles.json', function(err, data){ 
    fs.readFile('./template.html', function(err, data) {
...

解决:创建中间函数
server2.js

var http = require("http");
var fs = require("fs");

http.createServer(function(req, res){
  if(req.url == '/'){
    getTitles(res);
    }
}).listen(8000, "127.0.0.1");

// 获取标题
function getTitle(res) {
    fs.readFile("./titles.json", function(err, data) {
        if(err){
            hadError(err, res);
        }else{
            getTemplate(JSON.parse(data.toString()), res);
        }
    });
}

// 获取模板
function getTemplate(titles, res) {
    fs.readFile("./template.html", function(err, data) {
        if(err){
            hadError(err, res);
        }else{
            formatHtml(titles, data.toString(), res);
        }
    });
}

// 组装标题
function formatHtml(titles, tmpl, res) {
    var html = tmpl.replace("%", titles.join("</li><li>"));
    res.writeHead(200, {"Content-type": "text/html"});;
    res.end(html);
}

//处理错误
function hadError(err, res) {
    console.log(err);
    res.end("server error");
}

进一步解决:通过尽快返回较少嵌套
server3.js

var http = require("http");
var fs = require("fs");

http.createServer(function(req, res){
  if(req.url == '/'){
    getTitles(res);
    }
}).listen(8000, "127.0.0.1");

// 获取标题 return
function getTitle(res) {
    fs.readFile("./titles.json", function(err, data) {
        if(err){
            return hadError(err, res);
        }else{
            getTemplate(JSON.parse(data.toString()), res);
        }
    });
}

// 获取模板 return
function getTemplate(titles, res) {
    fs.readFile("./template.html", function(err, data) {
        if(err){
            return hadError(err, res);
        }else{
            formatHtml(titles, data.toString(), res);
        }
    });
}

// 组装标题
function formatHtml(titles, tmpl, res) {
    var html = tmpl.replace("%", titles.join("</li><li>"));
    res.writeHead(200, {"Content-type": "text/html"});;
    res.end(html);
}

//处理错误
function hadError(err, res) {
    console.log(err);
    res.end("server error");
}

事件监听

事件发射器:会触发事件并在那些事件触发时能处理他们。(如HTTP服务器、TCP服务器、流...)

  1. 用on响应事件
var net = require("net");
var server = net.createServer(function(socket) {
   socket.on("data", function(data) {
       socket.write(data);
   });
});
server.listen(8888);
  1. 用once响应一次性事件
var net = require("net");
var server = net.createServer(function(socket) {
    socket.once("data", function(data) {  //data事件只被处理一次
        socket.write(data);
    });
});
server.listen(8888);

3.创建事件发射器(EventEmitter):

var EventEmitter = require("events").EventEmitter;
var channel = new EventEmitter();  //事件发射器
channel.on("join", function() {  //添加监听器
    console.log("welcome");
});
channel.emit("join");  //触发事件

简单的发布/预订系统(PUB/SUB)

var events = require("events");
var net = require("net");

var channel = new events.EventEmitter();
channel.clients = {};
channel.subscriptions  = {};

//添加一个新增用户的监听器
channel.on("join",function(id, client) {
    this.clients[id] = client;  //将新用户加入用户表中
    this.subscriptions[id] = function(senderId, message) {  //每个用户可以向除了自己之外的用户发送信息
        if(id != senderId){
            this.clients[id].write(message);
        }
    }
    this.on('broadcast',this.subscriptions[id]); //为每一个用户添加一个发送广播信息的监听器
});

//添加用户断开连接的监听器
channel.on("leave",function(id) {
    channel.remoteListener('broadcast',this.subscriptions[id]);
    channel.emit("broadcast", id, id + " has left this chat.\n");
});


//定义服务器
var server = net.createServer(function(client) {
    var id = client.remoteAddress + ':' + client.remotePort; //将新用户id定义为该用户"ip:端口号"
    client.emit("join",client); //触发新增用户事件
    client.on("data",function(data) {  //当有用户发送消息时,触发broadcast事件,指明用户的id和消息
        data = data.toString();
        channel.emit('broadcast', id, data);
    });
    client.on("close",function() {
        channel.emit("leave",id);
    });
});
server.listen(8888);


异步开发难题

  • ex2
function foo(cb){
  settimeout(cb, 200);
}
var color = "green";
foo(function(){
  console.log(color);
});
color = "red";

结果:red
原因:ex2是异步执行的例子,在console.log执行之前,color的值从green变更为red,200ms后color的值为red。
解决:利用闭包函数保留全局变量的值

  • ex3
function foo(cb){
  settimeout(cb, 200);
}
var color = "green";
(function(color){
  foo(function(){
    console.log(color);
  })
})(color);
color = "red";

结果:green
原因:对foo函数的调用封装到了一个匿名函数里面,这个匿名函数会马上执行,同时把当前的color的值(green)传给他。color变成了匿名函数的参数,即这个匿名函数内部的本地变量,不受外部全局变量的影响。

异步逻辑的顺序化——流程控制

  1. 串行流程控制
    参考上文server2.js
  2. 并行流程控制
  • ex4 并行计算文档文件夹text 里面所有文档的单词出现的次数
var fs = require("fs");
var completeTaskes = 0;
var tasks = [];
var wordCounts = {};
var filesDir = "./text";

function checkIfComplete() {
    completeTaskes++;
    if(completeTaskes == tasks.length){
        for(var index in wordCounts){
            console.log(index + ": " + wordCounts[index]);
        }
    }
}

function countWordInText(text) {
    var words = text.toString().toLowerCase().split(/\w+/).sort();
    for(var index in words){
        wordCounts[word] = (wordCounts[word]) ? wordCounts[word] + 1 : 1;
    }
}

fs.readDir(filesDir, function(err, files) {
    if (err) throw err;
    for(var index in files){
        var task = (function(file) {
            return function() {
                fs.readFile(file, function(err, text) {
                    if(err) throw err;
                    countWordInText(text);
                    checkIfComplete();
                })
            }
        })(filesDir + "/" + files[index]);
        tasks.push(task);
    }
    for(var task in tasks){
        tasks[task]();
    }
});

相关文章

  • NodeJS——异步编程

    1. 回调:通常用来处理一次性响应事件。(如指定一个回调函数来确定如何处理数据库查询结果)2. 事件监听:通常用来...

  • nodeJS回调函数

    NodeJS异步编程的直接体现就是回调函数。 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。回调函...

  • nodejs笔记-异步编程

    1.函数式编程 1.1高阶函数 函数参数只接受基本数据类型或者对象引用,返回值也是基本数据类型和对象引用。 高阶函...

  • 回调函数

    nodejs异步编程的体现就是回调异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。回调函数在完成任务...

  • nodejs——事件驱动编程

    事件驱动编程/异步编程——当事件发生时由系统调用的函数来取代应用返回值的编程风格,这个nodejs的显著特征之一。...

  • nodejs之async异步编程

    async/await 一般和 Prmoise 一起使用。下面的代码是一个生成 Promise 对象的工具方法。 ...

  • Promise又一个异步编程解决方案?!

    前言 在NodeJS开发中,各种异步操作是不可避免的(好吧,这句明显是废话...)。开发人员在异步编程上有各种各样...

  • Nodejs 异步编程解决方案

    1. PubSub事件 事件监听模式是一种广泛应用于异步编程的解决方案,是回调函数的事件化,又称发布订阅模式。关于...

  • nodejs深入学(5)异步编程

    前言 上一章讲解了node如何通过事件循环实现异步,包括与各种IO多路复用搭配实现的异步IO已经与IO无关的异步A...

  • 47个在线实例让你迅速掌握NodeJs异步编程

    自从掉进了NodeJs的海洋之后,也陆续有小伙伴留言来咨询一些NodeJs的问题,其中大多数是关于异步编程的。 关...

网友评论

      本文标题:NodeJS——异步编程

      本文链接:https://www.haomeiwen.com/subject/sqtkfftx.html