http概念进阶

什么是回调?
回调是异步编程的基础方法,当需要按照顺序执行异步逻辑的时候,一般采取后续传递的方式,也就是将后续逻辑封装在回调函数中作为起始函数的参数,逐层去嵌套。

function lean(something){
   console.log(something)
}

function we(callback,something){
    something+=' is cool'
    callback(something)//结果传递给回调函数

}

we(lean,'node')//具名函数

we(function(something){//传递匿名函数
    console.log(something)

},'jade')

什么是同步
同步就是执行一个任务,后一个任务等待前一个任务结束,然后再执行,程序执行的顺序与任务的排列顺序是一致的,比如js是单线程的,因此js代码是按照顺序执行的,即便是js同步下载多个文件,也得按照顺序执行,一旦js里面出现死循环的代码,那么页面就会被阻塞在这个地方,后面的js代码就不会被执行到 比如打电话预约座位,发现没有座位了,店员就挂电话开始查找有没有座位,查找,查找…等待一会儿之后,店员通过回电话,开始执行回调函数来解决你的问题(是否有座位)。
什么是异步
异步执行任务,如果发现任务阻塞,js执行setTimeOut,毫秒时间作为其中的一个参数,然后自动执行另外一个函数,不会一直等待。

//代码按照顺序执行,结果看就是1
var c = 0
function printIt(){
    console.log(c)
}

function plus(){
    c+=1
}
plus()
printIt()
//加上耗时操作,没有加回调函数,结果不改变
var c = 0
function printIt(){
    console.log(c)
}

function plus(){//加上耗时操作,没有调用回调函数,所以js一直阻塞在这里,不能执行,所以结果还是 0,没有变化。
    setTimeout(function(){
          c+=1
    },1000)

}
plus()
printIt()
var c = 0
function printIt(){
    console.log(c)
}
function plus(callback){
    setTimeout(function(){
          c += 1
          callback();//5s之后timeout,开始调用回调函数
    },5000)
}

plus(printIt)

什么是io
磁盘的写和读,在nodejs里面就是为文件系统,数据库资源提供接口,发送请求的时候,不用等待硬盘,当硬盘准备好了,非阻塞接口就会通知处理
什么是阻塞/非阻塞
阻塞,在电话预定座位的时候,你将自己挂起,等待…直到等到有座位的信息的时候才挂电话。
非阻塞,打电话预定座位,没有等店员回答你是否有结果,你就挂电话了,该干啥干啥。你也可以过几分钟过来催一下,看看店员有没有搞定。
什么是事件
点击,拖拽等都是事件
什么是事件驱动
有事我叫你,没事别烦我–当需要的时候才会被调用。
事件循环
主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
《http概念进阶》
上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在”任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,依次执行那些事件所对应的回调函数。
Node.js的Event Loop
Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境。
《http概念进阶》
根据上图,Node.js的运行机制如下。

(1)V8引擎解析JavaScript脚本。

(2)解析后的代码,调用Node API。

(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。

(4)V8引擎再将结果返回给用户。

除了setTimeout和setInterval这两个方法,Node.js还提供了另外两个与”任务队列”有关的方法:process.nextTick和setImmediate。它们可以帮助我们加深对”任务队列”的理解。

process.nextTick方法可以在当前”执行栈”的尾部—-下一次Event Loop(主线程读取”任务队列”)之前—-触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。setImmediate方法则是在当前”任务队列”的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像。请看下面的例子(via StackOverflow)。

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED

上面代码中,由于process.nextTick方法指定的回调函数,总是在当前”执行栈”的尾部触发,所以不仅函数A比setTimeout指定的回调函数timeout先执行,而且函数B也比timeout先执行。这说明,如果有多个process.nextTick语句(不管它们是否嵌套),将全部在当前”执行栈”执行。

现在,再看setImmediate。

setImmediate(function A() {
  console.log(1);
  setImmediate(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0);

上面代码中,setImmediate与setTimeout(fn,0)各自添加了一个回调函数A和timeout,都是在下一次Event Loop触发。那么,哪个回调函数先执行呢?答案是不确定。运行结果可能是1–TIMEOUT FIRED–2,也可能是TIMEOUT FIRED–1–2。

令人困惑的是,Node.js文档中称,setImmediate指定的回调函数,总是排在setTimeout前面。实际上,这种情况只发生在递归调用的时候。

setImmediate(function (){
  setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
  });

  setTimeout(function timeout() {
    console.log('TIMEOUT FIRED');
  }, 0);
});
// 1
// TIMEOUT FIRED
// 2

上面代码中,setImmediate和setTimeout被封装在一个setImmediate里面,它的运行结果总是1–TIMEOUT FIRED–2,这时函数A一定在timeout前面触发。至于2排在TIMEOUT FIRED的后面(即函数B在timeout后面触发),是因为setImmediate总是将事件注册到下一轮Event Loop,所以函数A和timeout是在同一轮Loop执行,而函数B在下一轮Loop执行。

我们由此得到了process.nextTick和setImmediate的一个重要区别:多个process.nextTick语句总是在当前”执行栈”一次执行完,多个setImmediate可能则需要多次loop才能执行完。事实上,这正是Node.js 10.0版添加setImmediate方法的原因,否则像下面这样的递归调用process.nextTick,将会没完没了,主线程根本不会去读取”事件队列”!

process.nextTick(function foo() {
  process.nextTick(foo);
});

事实上,现在要是你写出递归的process.nextTick,Node.js会抛出一个警告,要求你改成setImmediate。

另外,由于process.nextTick指定的回调函数是在本次”事件循环”触发,而setImmediate指定的是在下次”事件循环”触发,所以很显然,前者总是比后者发生得早,而且执行效率也高(因为不用检查”任务队列”)。
Http缓存机制

《http概念进阶》

    原文作者:HTTP
    原文地址: https://segmentfault.com/a/1190000003697890
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞