前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二十三期:一道面试题和三个个知识点

二十三期:一道面试题和三个个知识点

作者头像
terrence386
发布2022-07-15 10:06:38
5580
发布2022-07-15 10:06:38
举报
文章被收录于专栏:JavaScript高级程序设计

一道面试题

代码如下:

代码语言:javascript
复制
let a;

const b = new Promise((resolve,reject)=>{
  console.log('promise')
  resolve()
}).then(()=>{
  console.log('promise2')
}).then(()=>{
  console.log('promise3')
})..then(()=>{
  console.log('promise4')
})

a = new Promise((resolve,reject)=>{
  console.log(a)
  await b;
  console.log('after1')
  await a;
  resolve(true)
  console.log('after2')
})

console.log('end')

问: 最终打印出什么结果?

我原本想的答案是下面的结果:

代码语言:javascript
复制
//1. end
//2. promise
//3. type error

理由是,这能看出来道题其实考察的是下面的知识点:

  • 变量声明
  • promise 消息队列或者叫(微任务和宏任务)
  • async 和 await 的用法

let 声明的变量存在TMD暂时性死区的问题,所以已声明但未被赋值的变量如果直接使用,会报未定义的错。

我把这个代码执行了一遍,确实报错了,但是报的是这个:

代码语言:javascript
复制
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules

原来是我少写了个async,真实的问题应该是这样:

代码语言:javascript
复制
let a;

const b = new Promise((resolve,reject)=>{
  console.log('promise')
  resolve()
}).then(()=>{
  console.log('promise2')
}).then(()=>{
  console.log('promise3')
})..then(()=>{
  console.log('promise4')
})

a = new Promise(async (resolve,reject)=>{
  console.log(a)
  await b;
  console.log('after1')
  await a;
  resolve(true)
  console.log('after2')
})

console.log('end')

这次的结果是:

代码语言:javascript
复制
// promise
// undefined
// end
// promise2
// promise3
// promise4
// after1

思考一下什么为会出现这种结果?其实还是上面说的那三个知识点。但是要是真正理解上面的三个知识点,又需要理解下面的知识点:

JS的并发模型和事件循环

JavaScript 有个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务,这个模型与其他语言的模型截然不同。

上图中有三个部分:栈内存堆内存,和消息队列。这是js的一个基本模型。

栈内存有两个作用:1,存放基本类型数据。2.提供代码运行环境。

堆内存的作用:主要是存储引用类型的数据。

消息队列:一个JavaScript运行时包含了一个带处理消息的消息队列。每个消息都关联一个用于处理这个消息的回调函数。

在事件循环期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移除队列,并作为输入参数来调用与之关联的函数。

函数的处理会一直进行到执行栈再次为空为止,然后事件循环队列会处理队列中的下一个消息。

这里有个问题,消息是什么?个人理解消息就是事件的回调函数。

在浏览器里,每当一个事件发生并且有一个事件监听器绑定在该事件上时,一个消息就会被添加进消息队列。如果没有事件监听器,这个事件将会丢失。所以当一个带有点击事件处理器的元素被点击时,就会像其他事件一样产生一个类似的消息。

函数 setTimeout 接受两个参数:待加入队列的消息和一个时间值(可选,默认为 0)。这个时间值代表了消息被实际加入到队列的最小延迟时间。如果队列中没有其它消息并且栈为空,在这段延迟时间过去之后,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表示最少延迟时间,而非确切的等待时间。比如:

代码语言:javascript
复制
const s = new Date().getSeconds();

setTimeout(function() {
  // 输出 "2",表示回调函数并没有在 500 毫秒之后立即执行
  console.log("Ran after " + (new Date().getSeconds() - s) + " seconds");
}, 500);

while(true) {
  if(new Date().getSeconds() - s >= 2) {
    console.log("Good, looped for 2 seconds");
    break;
  }
}
// Good, looped for 2 seconds
//  Ran after 2 seconds

可以看到 执行代码后先输出了 Good, looped for 2 seconds,然后输出Ran after 2 seconds

因为代码执行到setTimeout发现它是一个消息,将它加入到了消息队列中,等到栈清空以后,才又接着处理这个消息。

宏任务和微任务的概念

Here “platform code” means engine, environment, and promise implementation code.

下面这个表格可以很清楚的描述宏任务和微任务的概念:

宏任务

微任务

谁发起

宿主环境(Node,浏览器)

平台引擎

具体事件

1. script (可以理解为外层同步代码)/n2. setTimeout/setInterval、3. UI rendering/UI事件,4. postMessage,MessageChannel,5. setImmediate,I/O(Node.js)

promise,MutationObserver,process.nextTick

运行顺序

在后

在前

触发新一轮tick

不会

async 和 await

async 关键字加到函数申明中,可以告诉我们返回的是 promise,而不是直接返回值。以往我们写promise的时候,需要在then的返回值中才能捕获我们想要的结果。

但是await可以直接捕获我们想要的结果。

简单来说:await 关键字使JavaScript运行时暂停于此行,允许其他代码在此期间执行,直到异步函数调用返回其结果。一旦完成,我们的代码将继续从下一行开始执行。

比如:

代码语言:javascript
复制
async getDataList=()=>{
  const data = await getOtherList() 
  return data.blob()
}

解析器会在此行上暂停,直到当服务器返回的响应变得可用时。此时 getOtherList() 返回的 promise 将会完成(fullfilled),返回的 response 会被赋值给 response 变量。一旦服务器返回的响应可用,解析器就会移动到下一行,从而创建一个Blob。Blob这行也调用基于异步promise的方法,因此我们也在此处使用await。当操作结果返回时,我们将它从getDataList()函数中返回。

那么又出现一个问题

Blob是什么?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaScript高级程序设计 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JS的并发模型和事件循环
    • 宏任务和微任务的概念
      • async 和 await
      • 那么又出现一个问题
      相关产品与服务
      消息队列 CMQ 版
      消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档