前端代码分析题(选择题、分析题)——JS事件循环分析、await和作用域分析

news2024/11/26 10:44:56

 Promise其实也不难-CSDN博客

Promise 的执行顺序分析

Promise 对象的执行是异步的,但其执行器函数内部的代码是立即执行的,而 then方法注册的回调函数则是在 Promise 状态改变后执行的

const myPromise = new Promise((resolve, reject) => {
  console.log('A');
  console.log('B');
});

myPromise.then(() => {
  console.log('C');
});

console.log('D');

A-》B-》D

const newPromise1 = new Promise((resolve, reject) => {
  console.log('A');
  resolve('B');
});
const newPromise2 = newPromise1.then(res => {
  console.log(res);
});
console.log('C', newPromise1);
console.log('D', newPromise2);

A-》C Promise{<fulfilled>:'B'}->D Promise{<pending>}->B

const newPromise = new Promise((resolve, reject) => {
  console.log('A');
  setTimeout(() => {
    console.log("timer start");
    resolve("succeed");
    console.log("timer end");
  }, 0);
  console.log('B');
});
newPromise.then((result) => {
  console.log(result);
});
console.log('C');

事件循环

A-》B-》C-》timer starter->timer end ->succeed

流程解读

  • 当执行到new Promise时,会立即执行Promise构造函数中的执行器函数(executor function),即传递给Promise构造函数的箭头函数
  • 在执行器函数内部,首先打印出'A'
  • 然后,setTimeout被调用,它设置了一个延时为0毫秒的定时器回调。由于setTimeout的回调被放入了宏任务队列(macrotask queue),它不会立即执行,而是等待当前执行栈清空并且微任务队列(microtask queue)也为空时,才会被执行
  • 紧接着,打印出'B'
  • 此时,Promise的执行器函数执行完毕,但Promise对象newPromise状态仍然是pending,因为resolve函数是在setTimeout的回调中调用的,而这个回调还没有执行
  • 紧接着,打印出'C'
  • 事件循环和微任务队列:到目前为止,执行栈已经清空,继续执行微任务队列、宏任务队列中的任务,执行setTimeout()内回调函数。
  • resolve方法会触发在微异步任务队列then()方法,因此先输出栈中数据timer end,再检查队列中待执行的任务resolve

事件循环(Event Loop):事件循环是JavaScript运行时环境中的一个循环机制,它不断地检查调栈用和任务队列。当调用栈为空时,事件循环会首先检查微任务队列,并执行其中的所有任务。只有当微任务队列为空时,事件循环才会检查任务队列,并执行其中的任务。(调用栈-》微任务队列-》宏任务队列)

  • 微任务队列:它专门用于处理如Promiseresolvereject回调、async/await等微任务。微任务的优先级高于宏任务。
  • 宏任务队列:用来存储准备好执行的回调函数,比如setTimeoutsetInterval的回调
  • 调用栈

Promise.resolve().then(() => {
  console.log('outerPromise');
  const innerTimer = setTimeout(() => {
    console.log('innerTimer')
  }, 0)
});

const timer1 = setTimeout(() => {
  console.log('outerTimer')
  Promise.resolve().then(() => {
    console.log('innerPromise')
  })
}, 0)
console.log('run');

run-》outerPromise-》outerTimer-》innerPromise-》innerTimer

 

then方法里面的return语句讲状态改为fulfilled! 

Promise.resolve().then(() => {
  return new Error('error')
}).then(res => {
  console.log("then: ", res)
}).catch(err => {
  console.log("catch: ", err)
})

//打印出then: Error: error

 TypeError: Chaining cycle detected for promise

const promise = Promise.resolve().then(() => {
  return promise;
})
promise.catch(console.err)
//输出TypeError: Chaining cycle detected for promise

promise异常处理

.catch(err=>{console.log(err)}) | .then(res)=>{console.log(res)},(err)=>{console.log(err)}

两个都可以用于处理,那个在先就先处理错误!!!!

Promise.reject('error')
  .then((res) => {
    console.log('succeed', res)
  }, (err) => {
    console.log('innerError', err)
  }).catch(err => {
    console.log('catch', err)
  })

innerError error

  1. Promise.reject('error'):创建一个被拒绝的 Promise,拒绝原因为 'error'

  2. .then((res) => { console.log('succeed', res) }, (err) => { console.log('innerError', err) })

    • 第一个回调函数(处理成功情况的)不会被调用,因为这个 Promise 已经被拒绝。
    • 第二个回调函数(处理拒绝情况的)会被调用,因为它专门用于处理 Promise 被拒绝的情况。因此,会执行 console.log('innerError', err),其中 err 的值为 'error'
  3. .catch(err => { console.log('catch', err) })

    • .catch() 方法是 .then(null, rejectionHandler) 的语法糖,专门用于捕获 Promise 链中未被处理的拒绝情况。
    • 然而,在这个例子中,.then() 方法已经包含了一个处理拒绝的回调函数,因此 .catch() 方法不会被调用。

console.log('catch', err) 也不会被执行, .then() 方法中已经包含了处理拒绝的逻辑

promise .finally()用法

被解决(fulfilled)或拒绝(rejected)之后,执行一些不依赖于 Promise 结果的代码。这个方法返回在JavaScript中,Promise.finally() 方法是一个非常有用的工具,它允许你在一个 Promise 对象一个新的 Promise该 Promise 的解决值(fulfilled value)或拒绝原因(rejection reason)与原始 Promise 相同

let promise = new Promise((resolve, reject) => {
  // 模拟一个异步操作
  setTimeout(() => {
    resolve('操作成功');
    // 如果改为 reject('操作失败'); 则会触发 catch 回调
  }, 1000);
});

promise
  .then(result => {
    console.log(result); // 操作成功
  })
  .catch(error => {
    console.error(error); // 如果Promise被拒绝,则会打印错误信息
  })
  .finally(() => {
    console.log('清理资源或执行其他不依赖于结果的代码');
    // 这里可以执行一些清理工作,比如关闭数据库连接、清除缓存等
  });
Promise.resolve('A')
  .then(res => {
    console.log('promise1', res)
  })
  .finally(() => {
    console.log('finally1')
  })
Promise.resolve('B')
  .finally(() => {
    console.log('finally2')
    return 'result'
  })
  .then(res => {
    console.log('promise2', res)
  })

//打印promise1 A、finally1、finally2、promise2 B

promise.all()方法(&&)

Promise.all() 是 JavaScript 中一个非常有用的方法,它接受一个包含多个 Promise 对象(或可迭代对象,如数组)的可迭代对象,并返回一个新的 Promise。这个新的 Promise 会在所有给定的 Promise 对象都成功解决(fulfilled)后解决,其解决值(fulfilled value)是一个数组,包含了所有原始 Promise 对象的解决值(按相同的顺序)。如果任何一个 Promise 对象被拒绝(rejected),则新的 Promise 会立即被拒绝,其拒绝原因(rejection reason)是第一个被拒绝的 Promise 的拒绝原因。

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    // 所有 Promise 都成功解决后执行这里的代码
    // “!!!values 是一个数组”,包含了 promise1, promise2, promise3 的解决值
  })
  .catch((error) => {
    // 如果有任何一个 Promise 被拒绝,则执行这里的代码
    // error 是第一个被拒绝的 Promise 的拒绝原因
  });
function runAsync(num) {
  return new Promise((resolve) => setTimeout(
    () => resolve(num, console.log(num)), 1000)
  );
}

function runReject(num) {
  return new Promise((resolve, reject) => setTimeout(
    () => reject(`Error: ${num}`, console.log(num)), 1000 * num)
  );
}

Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
  .then((res) => console.log(res))
  .catch((err) => console.log(err));
  • 1s后 runAsync(1)、runAsync(3)输出1、3
  • 2s号 runReject(2)输出2,状态为reject,promise.all()捕获错误,执行.catch() Error 2
  • 4s后runAsync(4)输出4

promise.race()方法[谁早听谁的)]

Promise.race 是 JavaScript 中 Promise 对象的一个静态方法。它接收一个可迭代对象(比如数组)作为参数,该可迭代对象包含多个 Promise 对象(或者非 Promise 值,这些值会被隐式地转换为已解决的 Promise)。Promise.race 方法会返回一个新的 Promise,这个新的 Promise 的状态(解决或拒绝)是由第一个解决(resolve)或拒绝(reject)的传入的 Promise 决定的。

function runAsync(num) {
  return new Promise(
    (resolve) => setTimeout(() => resolve(num, console.log(num)), 1000)
  );
}

function runReject(num) {
  return new Promise(
    (resolve, reject) =>
    setTimeout(() => reject(`Error: ${num}`, console.log(num)), 1000 * num),
  );
}

Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
  .then((res) => console.log('res: ', res))
  .catch((err) => console.log(err));

function runAsync(num) {
  return new Promise(
    (resolve) => setTimeout(
      () => resolve(num, console.log(num)), 1000
    )
  );
}

Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then((res) => console.log('res: ', res))
  .catch((err) => console.log(err));

async与await 

async 和 await 是 JavaScript 中用于处理异步操作的关键字,它们提供了一种更直观、更易于理解的异步编程方式。在底层,async 和 await 是基于 Promise 实现的,它们让异步代码看起来像是同步代码一样。

async 和 await 底层详解

  1. async 函数
    • 当一个函数被声明为 async 时,它会隐式地返回一个 Promise。
    • async 函数内部可以使用 await 关键字来等待其他 Promise 的解决。
    • 如果 async 函数内部没有显式地返回一个 Promise,那么它会返回一个已经解决的 Promise,其值为 undefined
    • 如果 async 函数内部抛出了一个异常,那么这个异常会被封装成一个被拒绝的 Promise。
  2. await 关键字
    • await 只能在 async 函数内部使用。
    • await 会暂停 async 函数的执行,直到它等待的 Promise 被解决或拒绝
    • 如果 await 的 Promise 被解决,那么 await 表达式的结果就是 Promise 的解决值。
    • 如果 await 的 Promise 被拒绝,那么 async 函数会立即抛出一个异常,这个异常可以被 try...catch 语句捕获。
    • await 不会阻塞整个事件循环,它只是暂停了 async 函数的执行,允许其他代码继续运行。
async function runAsync() {
  console.log("runAsync start");
  await asyncFunc();
  console.log("runAsync end");
}

async function asyncFunc() {
  console.log("do something");
}

runAsync();
console.log('start')
  • runAsync start
  • do something
  • start
  • runAsync end

同步代码的执行(start执行时机分析

  • 在 runAsync 被调用的同时(由于它是异步的),同步代码 console.log('start') 也被立即执行,打印 "start"
  • 重要的是要注意,虽然 runAsync 包含了异步操作,但它被调用后,控制权立即返回到事件循环,允许同步代码(如 console.log('start'))在 runAsync 的异步操作完成之前执行。

await仅阻塞当前函数不阻塞其他函数

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
  setTimeout(() => {
    console.log('async1 timer')
  }, 0)
}

async function async2() {
  console.log("async2 start");
  setTimeout(() => {
    console.log('async2 timer')
  }, 0)
  console.log("async2 end");
}

async1();
setTimeout(() => {
  console.log('outer timer')
}, 0)
console.log("run")
  • async1 start
  • async2 start
  • async2 end
  • run
  • async1 end
  • async2 timer
  • outer timer
  • async1 timer

 await堵塞当前函数,等promise被resolve或reject才执行后续代码

async function runAsync () {
  console.log('async start');
  await new Promise(resolve => {
    console.log('promise')
  })
  console.log('async end');
  return 'async result'
}
console.log('main start')
runAsync().then(res => console.log(res))
console.log('main end')
  • main start
  • async start
  • promise
  • main end

此await后promise未被resolve或reject,故runAsync后续函数不会被执行

await只关心它直接等待的那个 Promise 的状态

 await 表达式并不关心 .then() 返回的新 Promise 的状态;

它只关心它直接等待的那个 Promise 的状态。 

async function runAsync () {
  console.log('async start');
  await new Promise(resolve => {
    console.log('promise')
    resolve('promise resolve')
  }).then(res => console.log(res))
  console.log('async end');
  return 'async result'
}
console.log('main start')
runAsync().then(res => console.log(res))
console.log('main end')
  •  main start
  • async start
  • promise
  • main end
  • promise resolve
  • async end
  • async result

await后Promise被reject需处理错误

如果 await 的 Promise 被拒绝,那么 async 函数会立即抛出一个异常,这个异常可以被 try...catch 语句捕获,未捕获直接报错(UnhandledPromiseRejectionWarning:error)

async function runAsync () {
  await promiseFunc();
  console.log('async');
  return 'async result'
}

async function promiseFunc () {
  return new Promise((resolve, reject) => {
    console.log('promise')
    reject('error')
  })
}

runAsync().then(res => console.log(res))

仅仅输出promise,未捕获直接报错

  • process.nextTick()事件循环时是加入微任务队列 
  • promise中resolve()函数后代码也需执行
  • setTimeout也需考虑可能题目中不同函数的延迟时间不同

作用域分析

区分严格模式和非严格模式,this不同,严格模式this为undefined

箭头函数的this不可用apply|call等方法更改

var num = 10
var obj1 = {
  num: 20,
  print: () => {
    console.log(this.num)
  }
}
obj1.print()

var obj2 = { num: 30 }
obj1.print.apply(obj2)
//输出
//10
//10

 浏览器环境调用function.call(null),this指向全局对象window

function test() {
  console.log(this);
}
test.call(null);

new关键字

var obj = { 
  user: 'yupi', 
  print: function(){ 
    console.log(this.user); 
  } 
} 
obj.print()
new obj.print()
  • yupi
  • undefined(非严格模式),TypeError严格模式

new obj.print():这里的代码尝试使用 new 关键字来调用 obj 对象的 print 方法。但是,这里有一个重要的概念需要理解:new 关键字用于构造对象实例,它期望后面的调用是一个构造函数。

  • 创建新对象:当 new 关键字执行时,它会创建一个新的空对象,并将这个新对象的原型设置为构造函数的 prototype 属性。
  • 设置原型链:JavaScript引擎会将新创建的对象的内部原型(__proto__)设置为构造函数的prototype属性
  • 绑定this并执行构造函数:然后,JavaScript引擎会将构造函数作为普通函数调用,但此时this关键字的值会被设置为新创建的对象。这意味着在构造函数内部,你可以通过this来引用新对象,并向其添加属性和方法。构造函数中的代码会执行,可能会初始化对象的属性、调用其他方法等。

  • 返回新对象:构造函数执行完毕后,如果它没有显式地返回对象(非原始值),则 new 表达式会默认返回这个新创建的对象。

然而,obj.print 并不是一个构造函数,而是一个普通的方法。它并没有设计为返回一个对象(实际上它返回的是 undefined,因为它没有 return 语句)。此外,它也没有一个 prototype 属性(这是构造函数才有的)。因此,使用 new 来调用它并不是一个好的做法,并且可能会导致意外的行为或错误。

严格模式下尝试使用 new 来调用一个非构造函数会导致 TypeError

但在非严格模式下,JavaScript 引擎会尝试执行这个操作,但结果通常不是你所期望的。

**与此类似,箭头函数不能做构造函数**

  1. 没有this绑定
    箭头函数不绑定自己的this,它会捕获其所在上下文的this值作为自己的this值,并且这个绑定在函数创建时就已经确定了,之后不会改变。这意味着在箭头函数内部使用this时,它总是引用函数被定义时的上下文中的this,而不是函数被调用时的上下文。这与构造函数的行为不符,因为构造函数需要在被new调用时能够正确地绑定到新创建的对象上。

  2. 没有prototype属性
    构造函数有一个特殊的prototype属性,这个属性是一个对象,它包含了可以由构造函数的所有实例共享的属性和方法。当你使用new关键字调用构造函数时,新创建的对象会继承这个prototype对象。然而,箭头函数没有prototype属性,因此它们不能被用作构造函数来创建具有共享原型方法和属性的对象实例。

  3. 不能使用new关键字调用
    由于箭头函数没有prototype属性,并且它们的this绑定是固定的,因此尝试使用new关键字来调用箭头函数会导致错误。JavaScript引擎会抛出一个TypeError,指出箭头函数不是构造函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2237579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DAY24|回溯算法Part03|LeetCode:93.复原IP地址、78.子集、90.子集II

目录 LeetCode:93.复原IP地址 基本思路 C代码 LeetCode:78.子集 基本思路 C代码 LeetCode:90.子集II 基本思路 C代码 通过used实现去重 通过set实现去重 不使用used和set版本 LeetCode:93.复原IP地址 力扣代码链接 文字讲解&#xff1a;LeetCode:93.复原IP地…

ts 将100个元素,每行显示9个元素,然后显示出所有行的元素,由此我们延伸出一个项目需求的简单算法实现。

1、先看一下baidu ai出的结果&#xff1a; 2、我们将上面的代码修改下&#xff0c;定义一个数组&#xff0c;然后记录每行的行号及相应的元素&#xff1a; <template><div>console</div> </template> <script setup lang"ts"> import …

17、论文阅读:VMamba:视觉状态空间模型

前言 设计计算效率高的网络架构在计算机视觉领域仍然是一个持续的需求。在本文中&#xff0c;我们将一种状态空间语言模型 Mamba 移植到 VMamba 中&#xff0c;构建出一个具有线性时间复杂度的视觉主干网络。VMamba 的核心是一组视觉状态空间 (VSS) 块&#xff0c;搭配 2D 选择…

用 Python 从零开始创建神经网络(三):添加层级(Adding Layers)

添加层级&#xff08;Adding Layers&#xff09; 引言1. Training Data2. Dense Layer Class 引言 我们构建的神经网络变得越来越受人尊敬&#xff0c;但目前我们只有一层。当神经网络具有两层或更多隐藏层时&#xff0c;它们变成了“深度”网络。目前我们只有一层&#xff0c…

推荐一款功能强大的视频修复软件:Apeaksoft Video Fixer

Apeaksoft Video Fixer是一款功能强大的视频修复软件&#xff0c;专门用于修复损坏、不可播放、卡顿、画面失真、黑屏等视频问题。只需提供一个准确且有效的样本视频作为参考&#xff0c;该软件就能将受损视频修复到与样本视频相同的质量。该软件目前支持MP4、MOV、3GP等格式的…

Web前端开发--HTML语言

文章目录 前言1.介绍2.组成3.基本框架4.常见标签4.1双标签4.1.1.标题标签4.2.2段落标签4.1.3文本格式化标签4.1.4超链接标签4.1.5视频标签4.1.6 音频标签 4.2单标签4.2.1换行标签和水平线标签4.2.2 图像标签 5.表单控件结语 前言 生活中处处都有网站&#xff0c;无论你是学习爬…

[ DOS 命令基础 2 ] DOS 命令详解-网络相关命令

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

gitlab无法创建合并请求是所有分支都不显示

点击Merge Requests ------> New merge request 创建新的合并请求时&#xff0c;在Source branch和Target branch中一个分支都不显示 排查思路&#xff1a; 1.怀疑是权限问题。 发现只有我的一个账号出现&#xff0c;检查了账号的权限&#xff0c;尝试了master、develop角色…

【温度表达转化】

【温度表达转化】 C语言代码C代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 利用公式 C5∗(F−32)/9 &#xff08;其中C表示摄氏温度&#xff0c;F表示华氏温度&#xff09; 进行计算转化。 输出 输出一行&#x…

「QT」几何数据类 之 QPoint 整型点类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid…

[Linux]:高级IO

1. IO 理解 1.1 IO 的基本概念 I/O即输入/输出&#xff08;input/output&#xff09;&#xff0c;是计算机系统中极为关键的操作环节。 在经典的冯诺依曼体系结构框架下&#xff0c;其核心在于数据的传输流向界定了输入与输出的概念。具体而言&#xff0c;当把数据从诸如键盘…

【GeoJSON在线编辑平台】(2)吸附+删除+挖孔+扩展

前言 在上一篇的基础上继续开发&#xff0c;补充上吸附功能、删除矢量、挖孔功能。 实现 1. 吸附 参考官方案例&#xff1a;Snap Interaction 2. 删除 通过 removeFeature 直接移除选中的要素。 3. 挖孔 首先是引入 Turf.js &#xff0c;然后通过 mask 方法来实现挖孔的…

【ReactPress】React + antd + NestJS + NextJS + MySQL 的简洁兼时尚的博客网站

ReactPress 是使用React开发的开源发布平台&#xff0c;用户可以在支持React和MySQL数据库的服务器上架设属于自己的博客、网站。也可以把 ReactPress 当作一个内容管理系统&#xff08;CMS&#xff09;来使用。 前言 此项目是用于构建博客网站的&#xff0c;包含前台展示、管理…

ZISUOJ 2024算法基础公选课练习一(1)

前言、 又是一年算法公选课&#xff0c;与去年不同的是今年学了一些纯C&#xff08;而不是带类的C&#xff09; 一、我的C模板 1.1 模板1 #include <bits/stdc.h> using i64 long long;int main() {std::cin.tie(nullptr)->sync_with_stdio(false);return 0; } 1…

【1】虚拟机安装

1.安装VMware WorkStation Pro VMware下载地址&#xff1a; 密钥&#xff1a;YF390-0HF8P-M81RQ-2DXQE-M2UT6 2.新建虚拟机 centos7下载地址&#xff1a;centos-7.9.2009-isos-x86_64安装包下载_开源镜像站-阿里云

【SpringBoot】SpringBoot自带的Jackson入门使用

导入依赖 springboot自带的&#xff0c;挨个点进去&#xff0c;就能找到 自定义对象转换器 import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModu…

软件工程概论项目(一),git环境的配置和平台代码的拉取

距离软工概论项目答辩还有五个周的时间&#xff0c;需要做一个项目&#xff0c;把心得体会都做一个记录。以便以后进行回顾和反思 这里写目录标题 一、环境的配置gitbash 一、环境的配置 gitbash 安装gitbash&#xff0c;简单说两句&#xff0c;git用于多人协作和代码托管&am…

分布式数据库中间件mycat

MyCat MyCat是一个开源的分布式数据库系统&#xff0c;它实现了MySQL协议&#xff0c;可以作为数据库代理使用。 MyCat(中间件)的核心功能是分库分表&#xff0c;即将一个大表水平分割为多个小表&#xff0c;存储在后端的MySQL服务器或其他数据库中。 它不仅支持MySQL&#xff…

万字长文解读深度学习——循环神经网络RNN、LSTM、GRU、Bi-RNN

&#x1f33a;历史文章列表&#x1f33a; 深度学习——优化算法、激活函数、归一化、正则化深度学习——权重初始化、评估指标、梯度消失和梯度爆炸深度学习——前向传播与反向传播、神经网络&#xff08;前馈神经网络与反馈神经网络&#xff09;、常见算法概要汇总万字长文解读…

一文了解什么是腾讯云开发

关于云开发的猜想 说到云开发&#xff0c;作为开发者的大家是否大概就有了想法。比如说过去的开发工作都是在自己本地电脑的开发工具&#xff0c;比如IDEA开发工具进行开发的&#xff0c;开发完成后再部署到服务器测试以及上线。那么腾讯云开发&#xff0c;是不是就是不用本地…