解答这个题目之前,先回顾下JavaScript的事件循环(Event Loop)。

JavaScript的事件循环

事件循环(Event Loop):同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。流程可以参考下图。

上面的话里我们需要注意到Event Queue这里是分两种情况的,即宏任务(macrotask)微任务(microtask),当主线程任务完成为空去Event Quenu读取函数的时候,是先读取的微任务,当微任务执行完毕之后,才会继续执行宏任务。流程可以参考下图。

所以这个时候可以总结到事件循环中的执行顺序

  • 同步 > 异步
  • 微任务 > 宏任务

那么微任务和宏任务都有什么呢,简单总结下就是:

  • 微任务:Promiseprocess.nextTick
  • 宏任务:整体代码scriptsetTimeoutsetInterval

setTimeout、Promise、Async/Await详解

setTimeout

定时器,可以延迟执行,属于宏任务,在JavaScript事件循环中,执行优先级最低,可以运行下面的代码得到结果

console.log('script start')
setTimeout(function(){
console.log('settimeout')
})
console.log('script end') //执行结果: script start -> script end -> settimeout

解析一下上面的代码:

  • 同步执行,遇到setTimeout,将其放入异步队列中,跳过继续执行,输出script start -> script end
  • 当同步任务队列执行完毕,拿到异步队列中的setTimeout,输出settimeout

上面的题可以直接联想到另外一道经典的面试题就是setTimeout(fn,0)的作用和原因?

Promise

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行,打印p的时候,是打印的返回结果,一个Promise实例。resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。这个时候可以再运行一段代码查看结果

console.log('script start')
let promise1 = new Promise(function (resolve) {
console.log('promise1')
resolve()
console.log('promise1 end')
}).then(function () {
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end') //输出结果:script start->promise1->promise1 end->script end->promise2->settimeout

解析一下上面的代码

  • 同步执行script start
  • 因为Promise本身是同步的立即执行函数,所以输出promise1,resolve()的作用是改变Promise对象的状态,并不会阻断函数的执行,所以会执行输出promise1 end。then方法因为是异步回调微任务,所以会放入到微任务队列中。跳出执行
  • 遇到setTimeout,放入宏任务队列,跳过执行。
  • 输出script end,同步任务队列执行完毕,然后去微任务队列查看有无执行函数,获得promise1函数的then方法,输出promise2,此时微任务队列为空,然后去宏任务队列查看有无执行方法,输出settimeout。

async/await

async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。可以运行下面的代码查看结果

async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
} console.log('script start');
async1();
console.log('script end') //输出结果:script start->async1 start->async2->script end->async1 end

解析一下上面的代码:

  • 同步执行,输出script start
  • 执行async1()函数,输出async1 start,这是遇到await语句,执行await方法,但是后面的语句放入微任务队列。
  • 执行async2()函数,输出async2
  • 继续执行同步队列,输出script end。此时同步队列执行完毕,微任务队列查看有无执行函数或方法,输出async1 end
  • 此时微任务队列为空,然后去宏任务队列查看有无执行方法。

总结

settimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行; promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

最后,给大家提供个究极问题,自己思考下答案然后打印对比下吧

    async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');

“setTimeout、Promise、Async/Await 的区别”题目解析和扩展的更多相关文章

  1. 一道题理解setTimeout,Promise,async/await以及宏任务与微任务

    今天看到这样一道面试题: //请写出输出内容 async function async1() { console.log('async1 start'); await async2(); consol ...

  2. angular2 学习笔记 ( Rxjs, Promise, Async/Await 的区别 )

    Promise 是 ES 6 Async/Await 是 ES 7 Rxjs 是一个 js 库 在使用 angular 时,你会经常看见这 3 个东西. 它们都和异步编程有关,有些情况下你会觉得用它们 ...

  3. ES6 class setTimeout promise async/await 测试Demo

    class Person { async getVersion () { return new Promise((resolve, reject) => { setTimeout(functio ...

  4. vue使用技巧:Promise + async + await 解决组件间串行编程问题

    业务场景描述 大家都通过互联网投递过简历,比如在智联.58.猎聘等平台.投递心仪的职位前一般都需要前提创建一份简历,简历编辑界面常规的布局最上面是用户的个人基本信息,如姓名.性别.年龄.名族等,接着是 ...

  5. 事件循环 EventLoop(Promise,setTimeOut,async/await执行顺序)

    什么是事件循环?想要了解什么是事件循环就要从js的工作原理开始说起: JS主要的特点就是单线程,所谓单线程就是进程中只有一个线程在运行. 为什么JS是单线程的而不是多线程的呢? JS的主要用途就是与用 ...

  6. promise和async await的区别

    在项目中第一次遇到async await的这种异步写法,来搞懂它 项目场景 :点击登录按钮后执行的事件,先进行表单校验 this.$refs.loginFormRef.validate(element ...

  7. promise async await使用

    1.Promise (名字含义:promise为承诺,表示其他手段无法改变) Promise 对象代表一个异步操作,其不受外界影响,有三种状态: Pending(进行中.未完成的) Resolved( ...

  8. Promise,async/await解决回调地狱

    先说一下async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行. 写一个async 函数 as ...

  9. async包 ES6 async/await的区别

    最基本的async 包 ApCollection.find({}).toArray(function (err, aps) { var num = 0; async.whilst( function ...

随机推荐

  1. CSP-S 94 (sb lsc gc赛)

    不要问我为什么题解倒着写,因为在填坑! 关于这场比赛就是我sb的再现 考完试旁边_LH叱的一声说道:“lsc真**垃圾”; lsc:........确实很垃圾! ------------------- ...

  2. php memcache 缓存与memcached 客户端的详细步骤

    缓存服务器有Memcache.Redis,我主要介绍了PHP中的Memcache,从Memcache简介开始,详细讲解了如Memcache和memcached的区别.PHP的 Memcache所有操作 ...

  3. Spring Boot Actuator监控使用详解

    在企业级应用中,学习了如何进行SpringBoot应用的功能开发,以及如何写单元测试.集成测试等还是不够的.在实际的软件开发中还需要:应用程序的监控和管理.SpringBoot的Actuator模块实 ...

  4. 【Flume】Flume基础之安装与使用

    1.Flume简介 ​ (1) Flume提供一个分布式的,可靠的,对大数据量的日志进行高效收集.聚集.移动的服务,Flume只能在Unix环境下运行. ​ (2) Flume基于流式架构,容错性强, ...

  5. node.js和ionic

    1.安装node.js node -v  检测node文件是否安装成功 node --version  检查node版本号 2.Npm   node package manager 管理工具  管理模 ...

  6. Win32 COM组件 x Android Service

    有些书在介绍和讲解android的Service组件时,会使用后台服务一词,并且与运行在主线程的Activity相对.因为后台一词很容易误解,服务一直运行在后台?什么线程在运行?服务一直有条线程在运行 ...

  7. Oracle '26-2月 -19 03.34.47.000000 下午' 字符串日期解析

    Oracle数据库, 时间字段是varchar2类型, 存储了 '26-2月 -19 03.34.47.000000 下午' 格式(TIMESTAMP 数据类型)的字符串日期, 将其解析为yyyy-M ...

  8. 🔥《手把手教你》系列基础篇之4-python+ selenium自动化测试-xpath使用(详细教程)

    1. 简介 俗话说:磨刀不误砍柴工,因此在我们要开始写自动化脚本之前,我们先来学习和了解几个基本概念,在完全掌握了这几个概念之后,有助于我们快速上手,如何去编写自动化测试脚本. 元素,在这个教程系列, ...

  9. LMS自适应天线阵列设计 MATLAB

    在自适应天线课上刚刚学了LMS自适应阵,先出一个抢先版贴一下结果,抢先某个小朋友一步. 关于LMS的具体介绍,直接看wiki里的吧,解释的比书上简明:传送门:https://en.wikipedia. ...

  10. 【2018寒假集训 Day2】【动态规划】维修栅栏

    维修栅栏 问题描述: 小z最近当上了农场主!不过,还没有来得及庆祝,一件棘手的问题就摆在了小z的面前.农场的栅栏,由于年久失修,出现了多处破损.栅栏是由n块木板组成的,每块木板可能已经损坏也可能没有损 ...