1.node 异步编程
概念
- js是在单线程处理程序
- I/O需要异步的回调
- 前端异步可以消除单线程的阻塞
- 放在Node后段执行可以优化cpu性能,同时执行多个异步任务
串连执行异步
//定义测输出方法
const showTime = (str) => {
console.log(str)
}
1. 传统回调嵌套:地狱回调
exports.callback =() =>{
setTimeout( () => {
showTime("test1");
setTimeout( () => {
showTime("test2");
})
})
}
2. 使用promise
const promise = (name,delay = 100) => {
return new Promise(resolve => {
setTimeout( () => {
showTime(name);
resolve();
})
})
}
//每一次then 都要返回new Promise()对象
exports.promiseTest = () => {
promise('Promise1',600)
.then((res) => {
return promise('Promise2',500)
})
.then(
(res) => {
return promise('Promise3',100)
})
}
//错误写法,该写法无法按需执行 必须正常返回new Promise对象才可以串行执行
exports.promiseTest = () => {
promise('Promise1',600)
.then(promise('Promise2',100) )
.then(promise('Promise2',200) )
}
3.使用gennerator 实现异步
let generatorTest = () => {
const generator = function* (name) {
yield promise(name + 1,500)
yield promise(name + 2,100)
yield promise(name + 3,200)
yield promise(name + 4,400)
}
function myGen(gen) {
let promise1 = gen.next().value;
if(promise1){
promise1.then(
res => {
myGen(gen)
}
)
}
}
myGen(generator('hello'))
}
generatorTest();
4.使用event订阅发布 实现异步
class MyEvent {
fnMap = {}//通过对象的key value 方式存储key 与 fnList 回调的数组方法 关系
constructor(){
}
addEvent(key,fn){ //订阅事件
if(!(key in this.fnMap)){
this.fnMap[key] = []
}
this.fnMap[key].push(fn)
}
dispatchEvent(key,...arg){ //发布事件
if(!(key in this.fnMap)){
console.error(key,"事件未注册")
return;
}
this.fnMap[key].forEach(fn => {
fn(...arg);
});
}
}
let myEvent = new MyEvent();
let i = 0 //每次递增
let callFn = (name) => () => {
console.log("调用成功",name)
myEvent.dispatchEvent("end")
}
let fnList = [
callFn("test1"),
callFn("test2"),
callFn("test3"),
]
myEvent.addEvent("end", () => {
if(i < fnList.length ){
let callIndex = i //这里需要特殊处理 提前把 i++ 再执行fnList 里面的方法
i++
fnList[callIndex]() //也可以写成 fnList[i++]() 一步到位
//如果 i++ 写在这里
// fnList 会一直循环调用 callFn() -> dispatchEvent() -> addEvent() 死循环,以至于i++ 不能正常递增
}
})
myEvent.dispatchEvent("end")
// 输出结果:
// 调用成功 test1
// 调用成功 test2
// 调用成功 test3
2.node 串行实现
promisify
const fs = require('fs');
// 同步调用
const data = fs.readFileSync('./conf.js'); //代码会阻塞在这里
console.log(data);
// 异步调用
fs.readFile('./conf.js', (err, data) => {
if (err) throw err;
console.log(data);
})
// promisify
const {promisify} = require('util')
const readFile = promisify(fs.readFile)
//1.使用then
readFile('./conf.js').then(data=>console.log(data.toString()))
//2.使用async await
(async ()=> {
const readFile = promisify(fs.readFile)
let data = await readFile('./conf.js');
console.log(data.toString())
}
)();
3.node + io
buffer
为数据缓冲对象,是一个类似数组结构的对象,可以通过指定开始写入的位置及写入的数据长度,往其中写入二进制数据
//创建长度为10个字节的 buf
const buf1 = Buffer.alloc(10)
console.log(buf1)
//创建字符集为ascii 为 a 的
const buf11 = Buffer.from("a")
console.log(buf11)
//创建字符集为 utf-8 的 xxx测试
const buf2 = Buffer.from("xxx测试")
console.log(buf2)
//拼接两个buff对象
const buf3 = Buffer.concat([buf1,buf2])
console.log(buf3)
//拼接两个buff对象
buf1.write("ggg")
console.log(buf1.toString())
//打印对应的字符集内容
console.log(buf1)
stream
是对buffer对象的高级封装,其操作的底层还是buffer对象,stream可以设置为可读、可写,或者即可读也可写,在nodejs中继承了EventEmitter接口,可以监听读入、写入的过程。具体实现有文件流,httpresponse等
//通过流复制图片
const fs = require('fs')
//创建流
const rs = fs.createReadStream("./img.png")//被复制的图片
const ws = fs.createWriteStream("./img42.png")//创建后的图片
rs.pipe(ws)
//服务器 图片处理:
//响应图片请求
const {url, method, headers} = request;
else if (method === 'GET' && headers.accept.indexOf('image/*') !== -1) { fs.createReadStream('.'+url).pipe(response);//直接把流输出给返回的response
}
4. node 路径Path库 相对与绝对
如在 /Users/jason_pro/Desktop/test.js , 执行 node test.js
__dirname //获取test.js文件夹路径,输出: Users/jason_pro/Desktop/
__filename //获取test.js的完整绝对文件路径 输出: Users/jason_pro/Desktop/test.js
path
var path = require("path")
path.resolve() === __dirname 意思一样 Users/jason_pro/Desktop/
path.resolve(__dirname) === __dirname 意思一样 Users/jason_pro/Desktop/
path.resolve(__dirname,'dist/') === __dirname + 'dist/' Users/jason_pro/Desktop/dist
* 多个地址则从左边第一个开始,依次被右边的路径合并,如:
path.resolve('/dist','aaa/','bbb/') ,输出 /dist/aaa/bbb
* 遇到/开头,则左边的原来的路径会被覆盖,如:
path.resolve('/dist','/aaa/','bbb/') ,输出 /aaa/bbb
* 遇到../开头,向上跳一级
path.resolve('/dist/aaa/bbb','../ccc/','bbb/') ,输出 /dist/aaa/ccc/bbb/
注意:
path.resolve('/dist') 如果加入 '/'开头 则 原来的绝对路径失效,输出 /dist
path.resolve('/dist','aaa/') //合并两个地址 ,输出 /dist/aaa
* 结尾的/ 可以省略
如:path.resolve('/dist','aaa/') = path.resolve('/dist','aaa')
5.node + http
Content-Type的类型
- html网页 : text/html
- text文本 : text/plain
- json格式 : application/json;
- 图片png :image/png
- pdf格式 :application/pdf
const http = require('http')
const fs = require("fs");
const server = http.createServer((request, reponse) => {
const { url, method, headers } = request;
if (url === "/") {//首页
reponse.statusCode = 200
reponse.setHeader("Content-Type","text/html")
fs.readFile("./index.html",(err,buf) => {
reponse.end(buf)
})
} else if (url === "/userInfo") {//调用接口
reponse.writeHead("200", { "Content-Type": "application/json" })
const json = {"aa":222,b:3333}
reponse.end(JSON.stringify(json))
} else if (headers.accept.indexOf("image/") !== -1) {//图片流处理
fs.createReadStream("./"+url).pipe(reponse)
}
})
server.listen(3000, ()=>{
console.log("服务启动成功")
})
6.node + http + socket.io
WebSocket 与 Socket.IO的关系
WebSocket是H5的规范,是一个标准的h5的socket协议
Socket.IO 是多种轮询机制以及其他实时通信方式,并封装了通用的接口。 这些方式包含 Adobe Flash Socket、Ajax 长轮询、Ajax multipart streaming 、持久 Iframe、JSONP 轮询等 WebSocket只是其中的一种。 当 Socket.IO 检测到当前环境不支持 WebSocket 时,能够自动地选择最佳的方式来实现网络的实时通信。
例子
运行 node index.js
index.js
const express = require('express')()
const http = require('http').Server(express)
const socketIO = require("socket.io")(http)//由于参数只能是http 需要转换为传统的 http库
socketIO.on('connection', (socket) => { //每个打开页面的用户连接后的 闭包处理
// socket 为当前打开页面的客户端
//添加监听到消息方法
socket.on("chat message", (msg)=> {
//发送广播给所有人
socketIO.emit("chat message",msg);
})
socket.on("disconnection", () => {
console.log("链接已断开")
})
})
// 注意使用的是 http启动 应为stocketIO绑定在http上
http.listen(3000,()=> {
console.log("启动3000 成功")
})
index.html
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO chat</title>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script src="http://libs.baidu.com/jquery/2.1.1/jquery.min.js"></script>
<script>
$(function() {
var socket = io("http://127.0.0.1:3000");//由于是本地自己服务,所以可以简写 io(),
//当然也支持跨域,可以是 其他服务器上的地址如:api.baidu.com:3000
$("form").submit(function(e) {
e.preventDefault(); // 避免表单提交行为
socket.emit("chat message", $("#m").val());
$("#m").val("");
return false;
});
socket.on("chat message", function(msg) {
$("#messages").append($("<li>").text(msg));
});
});
</script>
</body>
</html>
node 实现数据爬虫
const originRequest = require("request");
const cheerio = require("cheerio");
const iconv = require("iconv-lite");
function request(url, callback) {
const options = {
// url: url,
encoding: null
};
originRequest(url, options, callback);
}
for (let i = 103234; i < 103238; i++) {
const url = `https://www.dy2018.com/i/${i}.html`;
request(url, function (err, res, body) {
console.log("body",body,err) //这里err为 unable to verify the first certificate 有可能出现 待处理
// const html = iconv.decode(body, "gb2312");
// const $ = cheerio.load(html);
// console.log($(".title_all h1").text());
});
}
//unable to verify the first certificate 有可能出现 待处理