← 返回目录

异步

第 83 / 154 卷日期:

认。

如果我们要取消事件,浏览器会询问用户是否确定。

readyState

如果我们在文档加载完成之后设置 DOMContentLoaded 事件处理程序,会发生什么?

很自然地,它永远不会运行。

在某些情况下,我们不确定文档是否已经准备就绪。我们希望我们的函数在 DOM 加载完成时执行,无论现在还是以后。

document.readyState 属性可以为我们提供当前加载状态的信息。

有 3 个可能值:

loading —— 文档正在被加载。

interactive —— 文档被全部读取。

complete —— 文档被全部读取,并且所有资源(例如图片等)都已加载完成。

所以,我们可以检查 document.readyState 并设置一个处理程序,或在代码准备就绪时立即执行它。

异步

异步解决方案

同步操作:顺序执行,同一时间只能做一件事情。缺点是会阻塞后面代码的执行。

异步:指的是当前代码的执行作为任务放进任务队列。当程序执行到异步的代码时,会将该异步的代码作为任务放进任务队列,而不是推入主线程的调用栈。等主线程执行完之后,再去任务队列里执行对应的任务。优点是:不会

阻塞后续代码的运行。

异步场景

定时任务:setTimeout、setInterval 网络请求:ajax请求、动态创建img标签的加载

事件监听器:addEventListener

回调

回调函数就是我们请求成功后需要执行的函数。

实现了异步,但是带来一个非常严重的问题——回调地狱。

事件发布/订阅

Promise

const promise = new Promise((resolve, reject) => {

.then((arg) => { console.log(`执行resolve,参数是${arg}`)

})

.catch((arg) => { console.log(`执行reject,参数是${arg}`)

})

.finally(() => { console.log('结束promise')

});

Promise.reject(2)

//.catch(err=>console.log("err1,",err))

.then(null, err => console.log("err1,", err)) //因为是rejected状态,执行then的第二个 callback,改变状态为fulfilled

.then(res => { console.log("then1", res)

}, null) //因为是fulfilled,于是执行第一个回调,不会去到下一步catch

//.catch(err=>console.log("err2,",err))

.then(null, err => console.log("err2,", err))

then实现链式操作减低代码复杂度,增强代码可读性。

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。

每个Promise都会经历的生命周期是:

进行中(pending) - 此时代码执行尚未结束,所以也叫未处理的(unsettled)

已处理(settled) - 异步代码已执行结束已处理的代码会进入两种状态中的一种:

已完成(fulfilled) - 表明异步代码执行成功,由resolve()触发已拒绝(rejected)- 遇到错误,异步代码执行失败,由reject()触发

方法

  1. Promise.all 传入多个异步请求数组,若all成功,进入fulfilled状态,若一个失败,则立即进入rejected状态。
  2. Promise.allSettled 传入多个异步请求数组,无论失败还是失败,都会进入fulfilled状态。
  3. Promise.race 以一个Promise对象组成的数组作为参数,只要当数组中一个Promsie状态变成resolved或者

rejected时,就调用.then方法。

事件循环

iterator中文名叫迭代器。它为js中各种不同的数据结构(Array、Set、Map)提供统一的访问机制。部署了 Iterator接口,就可以完成遍历操作。因此iterator也是一种对象,不过相比于普通对象来说,它有着专为迭

代而设计的接口。

iterator 的作用:

为各种数据结构,提供一个统一的、简便的访问接口;

使得数据结构的成员能够按某种次序排列;

ES6 创造了一种新的遍历命令for…of循环

iterator的结构:它有next方法,该方法返回一个包含value和done两个属性的对象(我们假设叫 result)。value是迭代的值,后者是表明迭代是否完成的标志。true表示迭代完成,false表示没有。iterator内部

有指向迭代位置的指针,每次调用next,自动移动指针并返回相应的result。

原生具备iterator接口的数据结构如下:

Array Map

Set String

TypedArray 函数里的arguments对象

NodeList对象

这些数据结构都有一个Symbol.iterator属性,可以直接通过这个属性来直接创建一个迭代器。也就是说, Symbol.iterator属性只是一个用来创建迭代器的接口,而不是一个迭代器,因为它不含遍历的部分。使用

Symbol.iterator接口生成iterator迭代器来遍历数组的过程为:

let arr = ['a','b','c'];

let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }

iter.next() // { value: 'b', done: false }

iter.next() // { value: 'c', done: false }

iter.next() // { value: undefined, done: true }

for ... of的循环内部实现机制其实就是iterator,它首先调用被遍历集合对象的 Symbol.iterator 方法,该方法返回一个迭代器对象,迭代器对象是可以拥有.next()方法的任何对象,然后,在 for ... of 的每次循环中,都将调用

该迭代器对象上的 .next 方法。然后使用for i of打印出来的i也就是调用.next方法后得到的对象上的value属性。

对于原生不具备iterator接口的数据结构,比如Object,我们可以采用自定义的方式来创建一个遍历器。

yield 1;

yield 2;

yield 3;

}

// generators可以像正常函数一样被调用,不同的是会返回一个 iterator let iterator = createIterator();

console.log(iterator.next().value); // 1

console.log(iterator.next().value); // 2

console.log(iterator.next().value); // 3

Generator 函数是一个普通函数,但是有两个特征:

function关键字与函数名之间有一个星号使用yield语句,定义不同的内部状态

Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象

(Iterator Object)

function* generator() {

yield 1;

}

console.dir(generator);

generator函数的返回值的原型链上确实有iterator对象该有的next,这充分说明了generator的返回值是一个 iterator。除此之外还有函数该有的return方法和throw方法。

generator和普通的函数完全不同。当以function*的方式声明了一个Generator生成器时,内部是可以有许多状态的,以yield进行断点间隔。期间我们执行调用这个生成的Generator,他会返回一个遍历器对象,用这个对象上的方

法,实现获得一个yield后面输出的结果。

yield和return的区别:

都能返回紧跟在语句后面的那个表达式的值 yield相比于return来说,更像是一个断点。遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而

return语句不具备位置记忆的功能。

一个函数里面,只能执行一个return语句,但是可以执行多次yield表达式。

正常函数只能返回一个值,因为只能执行一次return;Generator 函数可以返回一系列的值,因为可以有任意多个yield

yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。

使用Generator的其余注意事项:

需要注意的是,yield 不能跨函数。并且yield需要和*配套使用,别处使用无效

function* createIterator(items) {

items.forEach(function (item) {

// 语法错误 yield item + 1;

});

}

箭头函数不能用做 generator

那么Generator到底有什么用呢?

因为Generator可以在执行过程中多次返回,所以它看上去就像一个可以记住执行状态的函数,利用这一点,写一个generator就可以实现需要用面向对象才能实现的功能。

Generator还有另一个巨大的好处,就是把异步回调代码变成“同步”代码

async/await

ES7提出的关于异步的终极解决方案

第一个版本说async/await是Generator的语法糖第二个版本说async/await是Promise的语法糖

关于async/await是Generator的语法糖:表明的就是aysnc/await实现的就是generator实现的功能。但是 async/await比generator要好用。

因为generator执行yield设下的断点采用的方式就是不断的调用iterator方法,这是个手动调用的过程。

针对generator的这个缺点,后面提出来自动执行next,但是仍然麻烦。而async配合await得到的就是断点执行后的结果。

因此async/await比generator使用更普遍。

async函数对 Generator函数的改进:

  1. 内置执行器:Generator函数的执行必须靠执行器,因为不能一次性执行完成。但是,async函数和正常的函

数一样执行,也不用使用 next方法,会自动执行。

  1. 适用性更好:yield命令后面是 Promise对象,但是 async函数的 await关键词后面,可以不受约束。
  2. 可读性更好:async和 await,比起使用 *号和 yield,语义更清晰明了。

关于async/await是Promise的语法糖:如果不使用async/await的话,Promise就需要通过链式调用来依次执行 then之后的代码:

而async/await的出现,就使得我们可以通过同步代码来达到异步的效果:

全站分卷索引 · 154 卷
  1. 1代码随想录知识星球精华(最强八股文)第五版(前端篇))
  2. 2html语义化
  3. 3src 和 href 的区别
  4. 4link 与 @import 的区别
  5. 5iframe
  6. 6CSS
  7. 7伪元素和伪类
  8. 8block,inline,inline-block
  9. 9重排、重绘和合成
  10. 10盒子模型
  11. 11margin 合并
  12. 12margin和padding 的值为百分比时
  13. 13浮动
  14. 14定位
  15. 15CSS选择器
  16. 16CSS选择器优先级
  17. 17选择器权重Specificity
  18. 18CSS属性继承
  19. 19不可/可继承属性
  20. 20display: none与 visibility: hidden的区别
  21. 21background-size
  22. 22client、offset&scroll
  23. 23CSS3新特性
  24. 24flex布局
  25. 25常见布局
  26. 26三栏布局
  27. 27垂直居中
  28. 28水平垂直居中
  29. 29CSS画三角形
  30. 30使chrome支持12px以下文字
  31. 31响应式布局
  32. 32媒体查询
  33. 33过渡与动画
  34. 34JavaScript 动画 和 CSS动画
  35. 35SCSS
  36. 36CSS性能优化
  37. 37有哪些数据类型
  38. 38判断数据类型
  39. 39类型转换
  40. 40parseInt&parseFloat
  41. 41浮点数求和步骤
  42. 42十进制小数转二进制
  43. 430.1+0.2 为什么不等于 0.3
  44. 44undefined 和 undeclared 的区别
  45. 45JavaScript NaN 属性
  46. 46isFinite()&isNaN()
  47. 47数组
  48. 48对原数组有影响
  49. 49对原数组无影响
  50. 50数组去重
  51. 51数组拍平
  52. 52ES6新增的数组方法
  53. 53判断是否存在某个值
  54. 54arguments为啥不是数组
  55. 55对象
  56. 56遍历对象的方法
  57. 57判断对象是否具有属性
  58. 58for in 和 for of 的区别
  59. 59继承方式
  60. 60instanceof
  61. 61深浅拷贝
  62. 62freeze属性
  63. 63函数
  64. 64函数柯里化
  65. 65函数的 length
  66. 66箭头函数和普通函数区别
  67. 67构造函数
  68. 68执行上下文
  69. 69作用域
  70. 70作用域和作用域链
  71. 71闭包和内存泄漏
  72. 72this
  73. 73关于 this 的指向问题
  74. 74改变this指向的方法(bind / call / apply)
  75. 75字符串
  76. 76DOM和事件
  77. 77哪些事件支持冒泡
  78. 78阻止冒泡
  79. 79HTMLCollection、NodeList
  80. 80dom.onclick 和 dom.addEventListener的区别
  81. 81e.target和e.currentTarget
  82. 82页面生命周期
  83. 83异步
  84. 84内存管理GC
  85. 85其它
  86. 86JavaScript 中日期时间格式转换
  87. 87input和object实现双向绑定
  88. 88JSON.stringify()
  89. 89escape、encodeURI&encodeURIComponent
  90. 90移动端滚动穿透和溢出
  91. 91ES6
  92. 92Map字典
  93. 93... 与rest
  94. 94proxy
  95. 95模块化
  96. 96Ajax、Fetch和Axios
  97. 97Webpack
  98. 98浏览器:事件循环
  99. 99浏览器内核
  100. 100浏览器同源策略
  101. 101输入URL回车后发生了什么
  102. 102HTTP
  103. 103HTTP 2.0
  104. 104浏览器存储
  105. 105鉴权
  106. 106Webstorage
  107. 107浏览器执行机制
  108. 108AST 抽象语法树
  109. 109性能优化
  110. 110CSS
  111. 111网络相关
  112. 112使用HTTP2.0
  113. 113图片
  114. 114CDN图片
  115. 115其他文件优化
  116. 116降低APP首页开屏渲染时间
  117. 117页面白屏
  118. 118HTTPS中间人攻击
  119. 119Web Worker
  120. 120Service Worker
  121. 121微前端
  122. 122Vue
  123. 123v-show 和 v-if 的区别
  124. 124双向数据绑定的原理
  125. 125Vue中的插槽使用
  126. 126Vue中的key
  127. 127Vue过滤器
  128. 128Vue中Scoped原理
  129. 129Vue中$nextTick的使用
  130. 130Vue中实现组件通信的方式
  131. 131Vue-router
  132. 132Vue路由传参
  133. 133前端路由
  134. 134Vuex
  135. 135虚拟DOM
  136. 136DIFF 算法
  137. 137Vue3和Vue2有哪些区别
  138. 138说一说你对React的理解
  139. 139React事件机制
  140. 140说一说你对react refs的理解
  141. 141react中key的作用
  142. 142组件开发
  143. 143有状态组件和无状态组件
  144. 144函数式组件和类组件的区别
  145. 145PureComponent&Component
  146. 146常见 Hooks
  147. 147状态管理
  148. 148redux toolkit
  149. 149React18新特性
  150. 150client中的createRoot取代以前的ReactDOM.render
  151. 151Suspense
  152. 152新的Hooks
  153. 153VDom一定更快吗
  154. 154Fiber