node之http
http协议是如何工作的:
1. http客户端发起请求,创建端口;
2. http服务器在端口监听客户端请求
3. http服务器向客户端返回状态和内容。
一个页面展示经历了什么?
- 域名解析;
- 查看有没有缓存DNS(浏览器自身的缓存和操作系统的自身的缓存)(chrome//net-internals/#dns);
- 读取本地host文件
- 浏览器发起DNS的一个系统调用
浏览器获得域名对应的IP地址后,发起HTTP经典的“三次”握手。
- A:你好,能听到我说话吗?
- B:听得到,听得到。
- A:开始聊天。
拓展:断开连接时四次挥手:
- A:我不说了。
- B:等一下,我还没说完。
- B:好了,我说完了,我也不说了。
- A:好的,拜拜。
TCP/IP连接建立起来后,浏览器就可以向服务器发送HTTP请求了,比如说用HTTP的GET方式请求一个根域里的一个域名,协议可以采用HTTP1.0的一个协议。
服务器端接收到了这个请求,根据路径参数,经过后端的一些 处理之后,把处理后的结果的数据(完整的HTML页面)返回给浏览器。
浏览器拿到完整的HTML页面代码之后,在解析和渲染这个页面的时候,里面的静态资源JS、CSS、图片、他们同样也是一个个HTTP请求,都需要经过上面的主要的七个步骤。
浏览器根据拿到的资源对页面进行渲染,最终把一个完整的页面呈现给用户。
写代码的过程中发现自己对很多概念的理解不是很准确,几个问题又出现在我的脑海:为什么这样写,node到底是什么,它的精髓在哪里?
总结过程中,发现以下几个概念的理解很重要
- 什么是回调?
回调是异步编程最基本的方法,后续逻辑封装在回调函数中作为起始函数的参数 逐层去嵌套,通过这种方式让程序用我们所期望的方式走完流程。 - 什么是异步?
程序的执行顺序和任务的排列顺序是不一致的,最基础的异步函数:setTimeout和setInterval - 什么是阻塞I/O?
input/output,程序执行过程中必然要进行很多I/O操作,读写文件、输入输出、请求响应等等。I/O操作时最费时的,至少相对于代码来说,在传统的编程模式中,举个例子,你要读一个文件,整个线程都暂停下来,等待文件读完后继续执行。换言之,I/O操作阻塞了代码的执行,极大地降低了程序的效率。 - 什么是非阻塞I/O?
理解了阻塞I/O,那么非阻塞I/O就很好理解了。I/O操作不会阻塞程序的执行,也就是在I/O操作的同时,继续执行其他的代码(得益于node的事件循环机制)。 - 什么是事件驱动?
当函数由于某个事件发生的时候而被调用执行的时候,这种函数调用的方式就叫做事件驱动。 - 什么是基于事件驱动的回调?
我们注册的回调就是基于事件驱动的回调。 - 什么是事件循环(Event Loop)?
如果有大量的异步操作,或者I/O耗时操作,以及定时器的延时操作,从而完成一些密集的任务,需要一个统一的机制来管理,这种机制就是事件循环。 (主线程从”任务队列”中读取事件,这个过程是循环不断的)。这个机制是什么样的呢:看了阮一峰老师的博客中关于Event Loop的一张图,醍醐灌顶:事件轮询的机制是,主线程 代码运行时会不断的产生堆和栈,而所有的异步回调函数会被压入栈中构成一个“回调队列”,当读取这个事件时,将调用与这个事件关联的JS函数代码,事件循环是先进先出的任务队列,整个任务队列是普通函数和回调函数构成的一个队列。
【以下内容摘自阮一峰老师博客】
但是,node中的运行机制如下,(与运行机制不同于浏览器)
(1)V8引擎解析JavaScript脚本。
(2)解析后的代码,调用Node API。
(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
(4)V8引擎再将结果返回给用户。
看了上面的概念之后,node的核心思想也不难理解:非阻塞,单线程,事件驱动。
异步之 回调函数方法 与 Promise对象方法 简单小实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.ball{
width: 40px;
height: 40px;
border-radius: 20px;
}
.ball1{
background-color: red;
}
.ball2{
background-color: green;
}
.ball3{
background-color: yellow;
}
</style>
</head>
<body>
<div class="ball ball1" style="margin-left: 0"></div>
<div class="ball ball2" style="margin-left: 0"></div>
<div class="ball ball3" style="margin-left: 0"></div>
回调函数
<script>
var ball1 = document.querySelector(".ball1");
var ball2 = document.querySelector(".ball2");
var ball3 = document.querySelector(".ball3");
function animate(ball,distance,callback) {
setTimeout(function () {
var marginLeft = parseInt(ball.style.marginLeft,10);
if(marginLeft === distance){
callback();
}else {
if(marginLeft < distance){
marginLeft++;
}else {
marginLeft--;
}
ball.style.marginLeft = marginLeft + 'px';
console.log(ball.style.marginLeft)
animate(ball,distance,callback)
}
},13)
}
animate(ball1,100,function () {
animate(ball2,200,function () {
animate(ball3,300,function () {
animate(ball3,150,function () {
animate(ball2,150,function () {
animate(ball1,150,function () {
})
})
})
})
})
})
</script>
Promise
<script>
var ball1 = document.querySelector(".ball1");
var ball2 = document.querySelector(".ball2");
var ball3 = document.querySelector(".ball3");
function promiseAnimate(ball,distance) {
return new Promise(function(resolve,reject) {
function ani() {
setTimeout(function () {
var marginLeft = parseInt(ball.style.marginLeft,10);
if(marginLeft === distance){
resolve()
}else {
if(marginLeft < distance){
marginLeft++;
}else {
marginLeft--;
}
ball.style.marginLeft = marginLeft + 'px';
console.log(ball.style.marginLeft);
ani()
}
},10)
}
ani()
})
}
promiseAnimate(ball1,100)
.then(function () {
return promiseAnimate(ball2,200)
})
.then(function () {
return promiseAnimate(ball3,300)
})
.then(function () {
return promiseAnimate(ball3,150)
})
.then(function () {
return promiseAnimate(ball2,150)
})
.then(function () {
return promiseAnimate(ball1,150)
})
</script>