js面试题之异步问题的深入理解

js中的宏任务与微任务

在面试过程中,基本面试官都会问你一些promise的问题,promise是es6的新内容,主要是用来优化异步的问题。笔试中经常会让你写一些promise和setTimeout的执行结果,这你就必须知道宏任务和微任务的概念了!

为什么要使用promise

如果你经历过以前的jquery开发项目,你会遇到以下问题:回调地狱

$.ajax({
	...
	success: function() {
		...
		$.ajax({
			...
			success: function() {

			}
		})
		...
	}
})

原因分析:

ajax请求嵌套,原因是我第二个请求依赖的参数在第一个请求的结果中,所以就得这么一直嵌套下去,ajax是异步的,不能再外面拿到里面的结果。这种代码导致的问题就是调试困难,耦合性非常高,后期改动一个地方就头疼!维护非常困难,代码可读性差。

于是乎就引入了promise对ajax进行了优化,axios就是基于promise的一个请求封装库,他们底层都是基于js原生的XMLHTTPREQUEST.

promise().then().catch()链式调用,多个请求可以promise().then().then()。

何为宏任务,何为微任务?

思考这个问题时你必须知道javascript是一种单线程的脚本语言,也就是它的代码正常只能从上往下依次执行,一次只能做一件事,异步是通过回调函数来实现的。为何不把js设计成多线程的语言呢?语言的用途决定了它的特性,js最初是用来做表单验证以及正则判断的,和操作DOM元素的。如果js有多个线程,一个执行DOM元素修改,另一个执行删除,那浏览器直接懵逼了,我到底该干啥???所以语言的用途决定了他的特性,但是浏览器是多线程的,除了主线程还有其他线程。

当js主程序执行时,先运行主程序上的同步代码,遇到setTimeout或setInterval就把它放入宏队列中,遇到promise的回调就把它放到微队列中,程序执行先执行主程序代码,再执行nextTick代码,然后微任务,最后宏任务,任务队列中的依次排队执行,async和await是配套使用的,await后面接一个promise对象,来看看下面这段代码:

 setTimeout(function(){console.log(1)},0); // 进入宏任务队列,最后执行宏任务
 new Promise(function(resolve,reject){
   console.log(2); //这句代码在promise构造器,同步执行
   resolve(); // 执行了resolve再把任务放入微队列
 }).then(function(){console.log(3)
 }).then(function(){console.log(4)});
 process.nextTick(function(){console.log(5)});
 console.log(6); // 主程序代码
 // 输出2,6,5,3,4,1

// 下面这个进阶代码
setTimeout(function(){console.log(1)},0); // 进入宏任务排序为1
new Promise(function(resolve,reject){
   console.log(2);
   // promise中执行完resolve()才会执行then(),而这里的resolve在宏任务里,执行完主程序代码后,还得先执行先进入宏队列中的程序
   setTimeout(function(){resolve()},0) // 进入宏任务排序为2
 }).then(function(){console.log(3)
 }).then(function(){console.log(4)});
 process.nextTick(function(){console.log(5)});
 console.log(6);
 // 输出的是 2 6 5 1 3 4

再看async and await中的执行顺序

代码如下(示例):

async function async1() {
  console.log(1);
  await async2();
  console.log(2); //这里要等await执行成功才会执行,进入微任务,排序1
}
async function async2() {
  console.log(3);
}
console.log(4); //主程序代码
setTimeout(function() {
  console.log(5);
}, 0) //进入宏任务,最后执行
async1();
new Promise(function(resolve) {
  console.log(6); // 这句同步执行
  resolve();
}).then(function() {
  console.log(7); //进入微任务,排序2
});
console.log(8); // 主程序代码
// 输出的是 4,1,3,6,8,2,7,5

总结

js是单线程语言,它的用途决定了他的特性,异步操作通过事件循环机制,先执行同步代码,然后微任务,最后宏任务,两个任务队列里的任务排队依次执行。await后面的代码必须等待promise返回结果再执行下面代码,await和async是generator函数的语法糖。

到此这篇关于js面试题之异步问题的文章就介绍到这了,更多相关js面试题之异步内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • js 异步操作回调函数如何控制执行顺序

    需求: fun A() { asyn(parm1, parm2, onsuccess(){ }) ;} fun B() {asyn(paem1, parm2, onsuccess(){}) ;} 函数B要求执行在函数A之后 异步执行 如果直接使用 A(); B(); 是不能够满足执行条件的. 考虑将B作为回调函数传递给A,然后A再执行的onsucess中执行B函数 A(B); 即可实现功能需求. js是单线程的. 1.调用函数时,如果参数多于定义时的个数,则多余的参数将会被忽略,如果少于定义时的

  • JavaScript系列之―同步还是异步?

    从今天开始,我会不定期的写一些关于JavaScript的东西,包括语言,应用等方面.组成JavaScript系列. 如果没有特殊的说明,这里假定JavaScript的执行环境是在浏览器(browser)当中的. 今天开始第一次,讨论一下同步和异步. 曾经查询过一些JavaScript的信息,发现google出来的结果都是询问JavaScript如何能够实现异步的代码. 而我,很不幸,查询的却是如何让JavaScript实现异步调用的同步(是不是挺起来很诡异). 首先说一下JavaScript当中

  • JS同步、异步、延迟加载的方法

    本文讲述了JS同步.异步.延迟加载的方法.分享给大家供大家参考,具体如下: 一:同步加载 我们平时使用的最多的一种方式. <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作.所以默认同步执行才是安全的.但这样如果js中有输出document内容.修改dom.重定向等行为,就会造成页面堵塞.所以一般建议

  • js前端面试之同步与异步问题详解

    前言 我本来是打算写一篇co源码精读(为啥读co,因为它短),然鹅发现自己存在一系列基础问题没有搞透彻,打算写一个js基础系列文章,总结自己的理解(copy),希望与你在学习路上一同进步.首先问问自己当面试官问到js中的同步和异步,这个问题该怎么回答?理解一个问题无非是what-why-how js同步和异步问题是什么-->为什么会产生异步问题-->如何解决. 一.JavaScript起源 技术的出现,和应用场景密切相关的.JavaScript诞生于1995年.当时,它的主要目的是处理以前由服

  • JavaScript中三种异步上传文件方式

    异步上传文件是为了更好的用户体验,是每个前端必须掌握的技能.这里我提出三点有关异步文件上传的方式. 使用第三方控件,如Flash,ActiveX等浏览器插件上传. 使用隐藏的iframe模拟异步上传. 使用XMLHttpRequest2来实现异步上传. 第一种使用浏览器插件上传,需要一定的底层编码功底,在这里我就不讲了,以免误人子弟,提出这点大家可以自行百度. 第二种使用隐藏的iframe模拟异步上传.为什么在这里说的是模拟呢?因为我们其实是将返回结果放在了一个隐藏的iframe中,所以才没有使

  • js中同步与异步处理的方法和区别总结

    在使用异步请求时,有时需要将异步请求的结果返回给另一个js函数,此种情况下会出现未等异步请求返回请求结果,该发送请求所在js函数已经执行完后续操作,即已经执行return ,这样会导致return的结果为空字符. 总结:若要在使用ajax请求后处理发送请求返回的结果,最好使用同步请求. 例如:以下例子会出现返回结果不正确的情况,因为ajax异步请求还未执行完,函数已经执行return了, 复制代码 代码如下: function fn(){ var result = " "; $.aja

  • 获取JavaScript异步函数的返回值

    今天研究一个小问题: 怎么拿到JavaScript异步函数的返回值? 1.错误尝试 当年未入行时,我的最初尝试: <script> function getSomething() { var r = 0; setTimeout(function() { r = 2; }, 10); return r; } function compute() { var x = getSomething(); alert(x * 2); } compute(); </script> 2.回调函数

  • Javascript 异步加载详解(浏览器在javascript的加载方式)

    一.同步加载与异步加载的形式 1. 同步加载 我们平时最常使用的就是这种同步加载形式: <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止了后续的解析,因此停止了后续的文件加载(如图像).渲染.代码执行. js 之所以要同步执行,是因为 js 中可能有输出 document 内容.修改dom.重定向等行为,所以默认同步执行才是安全的. 以前的一般建议

  • js异步加载的三种解决方案

    默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页头会导致加载很慢的话,是会严重影响用户体验的. (1) defer,只支持IE defer属性的定义和用法(我摘自w3school网站) defer 属性规定是否对脚本执行进行延迟,直到页面加载为止. 有的 javascript 脚本 document.write 方法来创建当前的文档内容,其他脚本

  • javascript 文件的同步加载与异步加载实现原理

    HTML 4.01 的script属性 charset: 可选.指定src引入代码的字符集,大多数浏览器忽略该值. defer: boolean, 可选.延迟脚本执行,相当于将script标签放入页面body标签的底部,js脚本会在document的DOMContentLoaded之前执行.除IE和较新版本的Firefox外,其他浏览器并未支持. language: 已废弃.大部分浏览器会忽略该值. src: 可选.指定引入的外部代码文件,不限制后缀名. type: 必选.指定脚本的内容类型(M

随机推荐