js同步异步 回调函数

学js这么久 只知道它是一个单线程的语言,同步异步听了这么多从未深思过。
在写ajax请求时再一次看见回调函数,想到在网上去查查详细的知识,没想到查出一大堆讲同步异步的,看了不少人的文章,这才感觉自己以前的理解是错的。自己又按着理解 整理总结了一下,不对的地方还希望大家及时更正。

js同步异步

同步 指的是一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。
异步 是指每一个任务有一个或多个回调函数,前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与排列顺序不一定一致,是异步的。

异步模式其实就是延迟处理。在和HTML交互的过程中,会需要一些IO操作(典型的就是Ajax请求,脚本文件加载),如果这些操作是同步的,就会阻塞其它操作,用户的体验就是页面失去了响应。


JS如何实现异步原理?

js是单线程的语言,同一时间只能做一件事。这样问题来了 异步和单线程不是自相矛盾的吗?

其实异步和单线程确实不能同时成为一个语言的特征,js选择成为单线程的语言它本身是不可以异步的。
但是js的宿主环境(浏览器 Node)是多线程的,宿主通过某种方式(事件驱动)使得js具备异步的属性。

浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程: JAVASCRIPT引擎线
程,渲染线程,浏览器事件触发线程。

  1. javascript引擎线程是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来 ,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序

  2. UI喧染线程负责渲染浏览器界面,当界面需要重绘 Repaint )或由于某种操作引发回流(reflow)吋.该线程就会执行。但需要注意Ul渲染线程与JS引擎是互斥的,当JS引擎抛行吋UI线程会被挂起, UI更新会被保存在一个队列中等到JS引擎空闲吋立即被执行。

  3. 事件触发线程,当一个事件被触发吋该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理.这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠棕点击、AJAX异步清求等,但由于JS的単线程关系所有这些事件都得排队等待JS引擎处理.

总的来说:当js触发到异步时,会将异步任务交给浏览器来执行,当执行有结果时会把异步任务的回调函数插入待处理队列的队尾。

UI线程和JS线程互斥实例

来看一段简单的代码:

<body>
<input type="text" name="input" value="" onkeydown="console.log(this.value);">  <!--事件会在用户按下一个键盘按键时发生--> //输入a 显示空格 输入aa 显示a

<input type="text" name="input" value="" onkeydown="var me=this;setTimeout(function(){console.log(me.value);},0)">    //输入a 显示a 输入aa 显示aa
</body>

分析:
第一个在keydown的时候,弹出来的是input里原来的value,(按下的时候先执行js一段代码,打印出value值,然后a才渲染出来)

而第二个在keydown的时候,却能弹出更新后的value,就是因为setTimeout,虽然他的delay设置为0,几乎是即时触发,但还是被添加到了执行队列后面,但就是这个过程,渲染已经完成了,当他回调函数执行时 ,输出来的
已经是更新后的value了。

注意:JS的工作机制是当线程空闲的情况下才会执行异步代码的回调
(即:当所有的同步任务执行完毕后才会执行异步任务的回调

常见的异步回调函数

1.点击事件
2.Ajax请求
3.定时器

浏览器处理点击事件的过程
浏览器开启事件触发线程,等待用户动作,事件触发线程解析为响应事件,转移到javascript引|擎线程,排队等候,等待
javascript引擎的处理。


一篇不错的文章:【关于js中的同步和异步】

里面的核心内容:

(1)所有同步任务都在主线程上执行,形成一个执行栈。
(2)主线程之外,还存在一个"任务队列"。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的 所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。


js回调函数

function foo(){
 var a = 10;
 return function(){
  a *= 2;
  return a;  
 }; 
}
var f = foo();
f(); //return 20.
f(); //return 40.

字面上的理解,回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。(即:主函数执行完后 再执行内部的回调函数)


回调函数与异步

我们都说 异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

(一直以来我都在混淆 异步和回调函数之间的关系)
我们来看这样一个例子:

var A = function (callback){
    callback();
    console.log("AAAAAAAA");
  }
  var B = function (){
    for (var i = 0; i < 10000; i++) {
    }
    console.log("BBBBBBBB");
  }
 A(B);
 
 //BBBBBB
 //AAAAAA

并不是说调用了回调函数就会实现异步操作,在这里callback函数仍然在主线程中执行,所以,调用callback,当前线程还是会去执行callback碰到IO操作才会真的执行异步

回调与同步、异步并没有直接的联系,回调只是一种实现方式,既可以有同步回调,也可以有异步回调,还可以有事件处理回调和延迟函数回调,这些在我们工作中有很多的使用场景

【关于js中的回调函数callback,通俗易懂】

猜你喜欢

转载自blog.csdn.net/qq_41853863/article/details/83242272