美文网首页
JS中异步事件

JS中异步事件

作者: 天涯西剑 | 来源:发表于2020-02-11 10:08 被阅读0次

JavaScript是单线程

JavaScript 之所以采用单线程,而不是多线程,跟历史有关系。JavaScript 从诞生起就是单线程,原因是不想让浏览器变得太复杂,因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。如果 JavaScript 同时有两个线程,一个线程在网页 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?是不是还要有锁机制?所以,为了避免复杂性,JavaScript 一开始就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

这种模式的好处是实现起来比较简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。JavaScript 语言本身并不慢,慢的是读写外部数据,比如等待 Ajax 请求返回结果。这个时候,如果对方服务器迟迟没有响应,或者网络不通畅,就会导致脚本的长时间停滞。

同步任务和异步任务

程序里面所有的任务,可以分成两类:同步任务(synchronous)异步任务(asynchronous)

同步任务是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。

异步任务是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有“堵塞”效应。

举例来说,Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行;如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数。

任务队列和事件循环

JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),里面是各种需要当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。)

首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。

异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。

JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了。这种循环检查的机制,就叫做事件循环(Event Loop)。维基百科的定义是:“事件循环是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。

回调函数

以上就是基本的概念介绍。

流程控制

一个问题是:如何控制异步任务实现类似同步的效果:异步任务1->异步任务2->异步任务3。如果不加以控制的话,3个异步任务的执行完成的顺序是不确定的。

这时候就需要用到回调函数,回调函数作为一个参数被传入前一个函数,使得前一个函数可以完成之后再调用这个回调函数。

function f1(callback){
    // do some things
    callback();
}
function f2(callback){
    // do some things
    callback();
}
function f3(){
    // do some things
}

// 三者联动起来是这样的
f1(function(){
    f2(function(){
        f3()
    })
});

执行过程是这样的:当f1被加入任务队列后,f2和f3并没有加入到任务队列。只有当f1执行完相关操作后,才会执行其回调函数f2,f2此时也才加入到任务队列,依次类推,f1、f2、f3实现了流程控制,本质上是通过控制回调函数加入到任务队列的时间点来控制流程

参数传递

上述代码回避了参数传递的问题。

function f1(arg,callback){
    // do some things
    callback();
}
function f2(arg,callback){
    // do some things
    callback();
}
function f3(arg){
    // do some things
}

// 三者联动起来是这样的
f1(arg1,function(){
    f2(arg2,function(){
        f3(arg3)
    })
});

其中f1和f2的callback的调用形式其实都是callback(),这意味着callback的参数列表为空,而f2和f3的参数列表并不为空,所以需要function(){f3(arg3)}来中转一下,根据需要参数的f2生成一个临时的匿名的不需要参数的function

相关文章

  • JS中异步事件

    JavaScript是单线程 JavaScript 之所以采用单线程,而不是多线程,跟历史有关系。JavaScri...

  • JS异步那些事 一 (基础知识)

    JS异步那些事 一 (基础知识)JS异步那些事 二 (分布式事件)JS异步那些事 三 (Promise)JS异步那...

  • JS异步那些事 三 (Promise)

    JS异步那些事 一 (基础知识)JS异步那些事 二 (分布式事件)JS异步那些事 三 (Promise)JS异步那...

  • JS异步那些事 四(HTML 5 Web Workers)

    JS异步那些事 一 (基础知识)JS异步那些事 二 (分布式事件)JS异步那些事 三 (Promise)JS异步那...

  • JS异步那些事 二 (分布式事件)

    JS异步那些事 一 (基础知识)JS异步那些事 二 (分布式事件)JS异步那些事 三 (Promise)JS异步那...

  • JS异步那些事 五 (异步脚本加载)

    JS异步那些事 一 (基础知识)JS异步那些事 二 (分布式事件)JS异步那些事 三 (Promise)JS异步那...

  • 浏览器中的事件循环与Node中的事件循环

    浏览器中的事件循环 JS线程读取并执行JS代码 执行JS代码的过程中,指定异步的操作给对应的线程处理 异步线程处理...

  • 事件循环机制

    事件循环(evenloop) 事件循环机制是宿主环境提供的。js中处理异步,增加了任务队列的概念(你不知道的js中...

  • 12.事件循环-微任务与宏任务

    事件循环机制js中的代码,其上下文进入执行栈之后,引擎会判断是不是异步操作(如DOM事件、timer、异步请求)。...

  • js笔记

    Javascript 事件循环: js解析方法时,将同步任务排队到执行栈中,异步任务排队到事件队列中。 事件队列分...

网友评论

      本文标题:JS中异步事件

      本文链接:https://www.haomeiwen.com/subject/djptfhtx.html