JavaScript异步—— ——异步机制

本文深入浅出地讲解了JavaScript中的异步编程概念及其核心机制,包括回调函数的应用及异步操作背后的执行原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


  什么是异步

  所谓异步,就是代码执行的顺序并不是按照从上到下的顺序一次性一次执行,而是在不同的时间段执行,一部分代码在“未来执行”。

  程序中现在运行部分和未来运行部分的关系就是异步编程的核心 ——— ——《YDNJ》

  setTimeout( ()={ console.log('1') },1000)

  console.log(2)

  //2

  //1

  如果没有异步的话,应该是按照顺序先执行,先输出1,再输出2,但是setTimeout是异步操作,在1000ms后的”将来执行“,所以先执行console.log(2)。

  回调函数

  回调函数我们都比较熟悉。如果有两个函数f1和f2,其中f1是异步函数,而f2是同步函数,如何让f2函数在f1之后执行?答案就是回调函数。

  幸运的是,基本绝大多数的异步操作都会给我们提供一个加入回调函数的接口,我们可以传入一个函数,在异步操作结束后立即”调用(为什么加引号在异步操作的机制中会探讨)。

  我们之前所举的例子中 ,setTimeout( ()={ console.log('1') },1000) 就是往异步函数setTimeout中传入回调函数,在计时1秒后调用。

  再比如我们常用的ajax:

  function f2(){

  console.log(我是回调函数)

  }

  $.ajax(www.baidu.com) //在百度页面打开控制台运行,省的跨域

  f2()

  //我是回调函数 f2先执行

  //undifined

  $.ajax(www.baidu.com,{success:f2})

  在ajax获取数据后调用f2。

  异步操作的机制

  异步操作时浏览器(或者其他环境)是如何工作的?回调函数在何时调用,如何调用?我们通过异步操作的机制的学习都能得到答案。

  在主程序结束和回调函数调用期间,是没有JS代码运行的,这是否意味着此时我们的浏览器是停止工作的?当然不是。

  其实,绝大多数异步操作的功能并不是完全由JS完成的,JS引擎不会计时,也不会发送和接受网络请求,这一切都是由浏览器(或者Node等工作环境)来完成的,js引擎只是通过调用浏览器给它的接口,命令浏览器完成这些功能。所以异步操作不代表不工作,只是该工作转交给浏览器处理,当浏览器处理完这部分操作后会告诉JS引擎来调用回调函数。

  然而这个告诉并不是我们想象的那样是浏览器主动对JS引擎说我已经处理好了,而是JS引擎每隔一段时间就问浏览器你有没有完成操作,一旦完成,就调用回调函数。这个过程不断重复就叫做Event Loop,如下图所示:

  解释一下这张图,当进程中的JavaSrcipt代码执行结束后,会检查任务队列中是否有任务,如果有任务,就立刻执行,如果没有任务,就过一段时间再次检查,循环往复。

  任务队列又是什么呢?浏览器每次完成一个异步任务,就会在一个队列(先进先出)中插入回调函数, 一旦EventLoop中有内容,并且此时没有JS程序运行,JS引擎就会调用该队列的第一个回调函数 ;但是如果此时有JS程序正在运行,需要等该JS代码运行完,然后才会依次调用任务队列中的回调函数。

  然后我们也就可以解释下面这个代码了:

  setTimeout('console.log(1)',0)

  console.log('2')

  输一下,结果居然是先输出2,再输出1,setTimeout不是设时间为0么,为什么没有立刻执行?其实计时器虽然设为0,会立即在任务队列中加入console.log(1),但是此时还有代码在运行,所以不能执行任务队列中的任务,等console.log('2')执行完毕,主程序执行完,才会调用任务队列中的console.log(1)。

  接下来来个更加详细的介绍图:

  我来解释一下,首先是左侧JS一栏,代码运行时,把代码加载到一个栈(stack)中,按照顺序执行,并且将数据存在堆(heap)中,要使用时调用。

  然后:

  1.在代码执行过程中,一旦遇到异步操作,会调用浏览器提供给JS引擎的接口(WebAPIS),让浏览器处理DOM、ajax、setTimeout等操作,然后JS引擎继续处理下面的JS,两者间的工作互不干扰。(接下来的2和3是同时进行的,部分先后)

  2.JS引擎把JS代码加载完毕,就检查任务列表中是否有任务要处理,有的话就执行,没有的话,等待一会再检查。

  3.浏览器一旦把异步操作处理完毕,就在任务队列中加入onClick,onLoad回调函数等 。 (注意这里不是具体传入的函数,而是onClick,onLoad,但是onClick、onLoad执行时会根据作用域等在堆中找到相应的函数值,然后执行)

  .

  4.重复上述过程

  最后再来个例子热热身:下面两行代码运行结果是否有区别

  var req = new XMLHttpRequest( );

  req.open(‘GET’ ,url);

  req. = function(){};

  req. = function(){};;

  req.send()

  var req = new XMLHttpRequest( );

  req.open('GET' ,url);

  req..send();

  req. = function(){};

  req. = function(){};;

  答案是肯定没有的。

  因为req.send是异步操作,在操作完成后,不能立即调用和,而是要等到主程序执行完毕,此时在上面两端代码中,两个函数都完成赋值。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值