什么是异步
计算机在设计上是异步的。
异步意味着事情可以独立于主程序流发生。
当你打开一个网页,网页载入的过程,你又打开了编译器,那么你在网页载入时启动了编译器的行为就是计算机的异步,
可以看出计算机时一个超大的异步程序集合,在计算机中你可以同时做很多事情而不必一件事情做完在做下一件,
但是在浏览器中不同,浏览器只有一条主线,它沿着主线按部就班的执行程序,在渲染一个网页时,总是自上而下的执行 html,head,body,以及其内部的元素,
当一个网页的头部太大,或者加载一个网页时因网络原因导致网页的头部渲染的太慢时,可能会出现很长时间的‘白屏’,在head渲染完成之前,body都不会开始渲染,
这对用户的体验时很糟的,而JavaScript 是一种单线程的语言,这表示它和浏览器一样只有一条主线,一次只能完成一件事情,作为用户我们都希望可以在交互上进行异步操作,就像计算机那样,同时可以执行多个操作,
而回调和异步函数就解决了这个需求,
回调和异步函数
在处理事件时,往往不能马上解决,需要用户的交互行为产生才能响应处理,
在一个网页中,同一时间可能有多个事件等待触发,同时事件在等待时,页面也可能还有其他的事情要做(例如,读取网络资源图片,视频等),
这个时候回调就显得很重要,这些待处理的事件全部放到一个事件循环的队列中,当触发事件时,这个队列响应并处理,而在等待触发事件的过程,浏览器可以执行其他操作,
回调函数曾经是 JavaScript 中实现异步函数的主要方式,
在加入了promise和async await之后,能够更好的控制异步的函数(不能立刻执行完),
通过回调和异步执行,JavaScript 的事件循环模型的一个非常有趣的特性是,它永不阻塞,
要注意alert提示还是会阻塞,当页面触发提示时,页面上的其他内容会被停止,直到alert结束
定时器,典型的异步回调函数
setTimeout(),setInterval()
setTimeout(() => {}, 1000);
setInterval(() => {}, 1000);
它们都不会立刻执行,而是会等待1秒,事实上即使这个时间参数为0,它也不会立即执行,而是被塞到事件循环中首个执行,这表示它会比不同函数更慢执行,
setTimeout(() => {console.log("这是异步函数的输出");
},0);(()=>{console.log("这是同步函数的输出");
})();
这里涉及到的事件循环,它总是排在同步函数的后面,即便参数是0ms,但在实际使用时,它确实会马上响应,这是因为计算机的算力很快,几乎可以在瞬间完成所有的同步操作,正常情况下很难感受到它所花费的时间
事件循环
js中有大量的异步事件,它们全部都被塞入到一个循环队列中,
一个循环结构大致可以理解成:
循环开始(回调的预处理工作+待执行的回调)--->
是否有新的回调或者事件需要执行--->
检查是否有新的回调或者事件->结束本次循环--->
下一次循环开始.......
有的时候,我们需要某些异步函数立刻执行,比如网络请求,我们需要先拿到结果,在进行渲染,这个时候就要求异步函数先执行,通过async和await,相当于可以不让异步函数进入事件循环,同时阻塞后面的函数执行,
process.nextTick
如果你有一个事件需要在所有其他事件之前执行,nodejs提供了process.nextTick
process.nextTick和定时器有些不同,它保证了它的回调会在事件循环(或者是下一个事件循环)之前执行,它的回调总是会被提到一个事件循环的顶部,
可以看到它的执行优先级比setTImeout(()=>{},0)还要高,
整体的执行顺序就是:同步函数--->process.nextTick--->setTImeout(()=>{},0)--->setImmediate--->其他异步函数