nodejs的异步编程
用回调处理一次性事件
a.json,我们要读取的文件内容
[
"标题一",
"标题二",
"标题三"
]
template.html模板文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Latest posts</title>
</head>
<body>
<ul>
<li>%</li>
</ul>
</body>
</html>
app.js
const http = require('http');
const fs = require('fs');
var server = http.createServer((req, res) => {
console.log(req.url)
if (req.url == '/') {
fs.readFile('./a.json', (err, data) => {
if (err) {
console.log(err)
}
else {
const titles = JSON.parse(data.toString());
console.log(titles)
fs.readFile('./template.html', (err, data) => {
if (err) {
console.log(err)
}
else {
const tmpl = data.toString();
const html = tmpl.replace('%', titles.join('</li><li>'));
res.writeHead(200, { 'Content-Type': 'text/html;charset=UTF8' });
res.end(html)
}
})
}
})
}
})
server.listen(3030, () => {
console.log('服务器启动了。。。')
})
使用nodejs的fs模块读取文件时习惯用相对路径,但是运行的时候出现了上述的错误,原因就是fs模块读取文件的相对路径是以启动server.js的位置为基准的,而不是以server.js文件的位置。
问题一:
{ Error: ENOENT: no such file or directory, open './a.json' errno: -2, code: 'ENOENT', syscall: 'open', path: './a.json' }
原因就是没有进入到该文件目录,启动服务器app.js,所以必须进入到服务器文件所在的目录进行启动,否则就要使用绝对路径读取文件
问题二:
嵌套了三层回调
http.createServer((req, res) => {...
fs.readFile('./a.json', (err, data) => {...
fs.readFile('./template.html', (err, data) => {
优化方法一:把回调函数用命名函数替换
const http = require('http');
const fs = require('fs');
var server = http.createServer((req, res) => {
getTitles(req, res);
})
function getTitles (req, res) {
if (req.url == '/') {
fs.readFile('./a.json', (err, data) => {
if (err) {
hasErr(err, res)
}
else {
getTemplate(data, res)
}
})
}
}
function getTemplate (data, res) {
const titles = JSON.parse(data.toString());
console.log(titles)
fs.readFile('./template.html', (err, data) => {
if (err) {
hasErr(err, res)
}
else {
formatHtml(data, titles, res)
}
})
}
function formatHtml (data, titles, res) {
const tmpl = data.toString();
const html = tmpl.replace('%', titles.join('</li><li>'));
res.writeHead(200, { 'Content-Type': 'text/html;charset=UTF8' });
res.end(html)
}
function hasErr (err, res) {
console.log(err);
res.end("Server error")
}
server.listen(3030, () => {
console.log('服务器启动了...')
})
最后的结果是:
优化方法二:
const http = require('http');
const fs = require('fs');
var server = http.createServer((req, res) => {
getTitles(req, res);
})
function getTitles (req, res) {
if (req.url == '/') {
fs.readFile('./a.json', (err, data) => {
if (err) return hasErr(err, res) //如果发生错误,直接返回,后面就没必要执行了
getTemplate(data, res)
})
}
}
function getTemplate (data, res) {
const titles = JSON.parse(data.toString());
console.log(titles)
fs.readFile('./template.html', (err, data) => {
if (err) return hasErr(err, res) //如果发生错误,直接返回,后面就没必要执行了
formatHtml(data, titles, res)
})
}
function formatHtml (data, titles, res) {
const tmpl = data.toString();
const html = tmpl.replace('%', titles.join('</li><li>'));
res.writeHead(200, { 'Content-Type': 'text/html;charset=UTF8' });
res.end(html)
}
function hasErr (err, res) {
console.log(err);
res.end("Server error")
}
server.listen(3030, () => {
console.log('服务器启动了...')
})
事件发射器处理重复事件
事件是通过监听处理的,监听器是跟事件相关的,当有事件出现时就被触发的回调函数。TCP socket的data事件,每当socket有新数据时就会被触发
socket.on('data',callback)
const net = require('net');
const server = net.createServer(socket => {
socket.on('data', data => {
socket.write(data)
})
})
server.listen(8000, () => {
console.log('started')
})
响应只应该发生一次的事件
const net = require('net');
const server = net.createServer(socket => {
socket.once('data', data => {
socket.write(data)
})
})
server.listen(8000, () => {
console.log('started')
})
事件发射器,PUB/SUB
const EventEmitter = require('events').EventEmitter;
const channel = new EventEmitter();
channel.on('join', () => {
console.log('welcome')
})
channel.emit('join') //welcome
channel.emit('join') //welcome
channel.emit('join') //welcome
异步逻辑的顺序
function asyncFunc (cb) {
setTimeout(cb, 200);
}
let color = 'blue'
asyncFunc(() => {
console.log(`the color is ${color}`) //the color is green
})
color = 'green'
用闭包冻结了color,asyncFunc异步函数被封装在以color为参数的匿名函数里,让其立即调用,就可以把当前的color值blue传递给它,color成了匿名函数的参数,也就是内部的本地变量,当外部的变量发生变化时,不影响里面的color
function asyncFunc (cb) {
setTimeout(cb, 200);
}
let color = "blue";
(color => {
asyncFunc(() => {
console.log('haha', color) //blue
})
})(color)
color = 'green'
异步逻辑的顺序化
有些任务只能在某些特定的任务之前或之后做,让一组异步任务顺序执行的概念被叫做流程控制。分为串行和并行
串行执行 : 任务一 =》 任务二 =》 任务三 =》 继续下去。。。(跟同步逻辑类似)
|| === 任务开始1 =》
并行执行: || === 任务开始 2 =》 =========》 所有任务结束后继续
|| === 任务开始 3 =》
用回调让任务顺序执行,setTimeout
setTimeout(() => {
console.log('1111')
setTimeout(() => {
console.log('222')
setTimeout(() => {
console.log('333')
}, 100)
}, 500)
}, 1000)
nelsen-mac:cb_event mac$ node async.js
1111
222
333
cnpm i async --save
const async = require('async');
async.series([
callback => {
setTimeout(() => {
console.log('111');
callback()
}, 1000)
},
callback => {
setTimeout(() => {
console.log('222');
callback()
}, 100)
},
callback => {
setTimeout(() => {
console.log('333');
callback()
}, 10)
}
])
nelsen-mac:cb_event mac$ node async.js
111
222
333