异步

异步概念

有很多场景存在。

setTimeout 会放到代码结束之后执行。
异步不会阻塞后面程序执行

单线程

  1. var a = true
  2. setTimeout(function(){a=false},100)
  3. while(true){console.log('x')}

因为js是单线程的,进入while之后会死循环,没有线程去跑定时器。

异步的场景

  • 定时器。setTimeout setInterval
  • 网络请求
  • promise
  • async/await

定时器

settimeout0 不是马上执行,是单线程的js忙完了其他逻辑才运行,是异步的

可以考虑 requestAnimationFrame 这个函数自带节流 16.6ms

promise

除了Promise,同一时期的计算机科学家还使用了“终局”(eventual)、“期许”(future)、“延迟”(delay)和“迟付”(deferred)等术语指代同样的概念。所有这些概念描述的都是一种异步程序执行的机制。

经过一段时间的发展,最终规定了 Promses/A+规范,也称为了 ES6 规范实现的范本。

api

Proimse 本身是引用类型,可以通过 new 来实例化。参数传入执行器。

实例状态机:

  • pending 待定,进行中
  • fulfilled/resolved/settled 解决、兑现
  • rejected 拒绝

  • pending 本身不一定非得变为另一个状态,但变到另外一个状态,不可再改变。

  • pending 的过程本身是私有的,不能被修改,不能检测。

实例打印
image.png
构造函数打印
image.png

在谈论具体api时候,我们假想存在下面的函数:

  1. class Promise{
  2. then(){}
  3. catch(){}
  4. finally(){}
  5. static all(){} // 一组Promise全部解决之后再解决
  6. static allSettled(){} //
  7. static race(){} // 一组Promise中最先出结果的无论是res还是rej的
  8. static reject(){}
  9. static resolve(){}
  10. }

EventLoop

浏览器中的 eventloop

js遇到异步函数,会放到task里,等到当前执行队列空,开始执行task。

本质上,异步还是同步演变。

task分为两种:

  • 微观任务micro task 称为 job
  • 宏观任务 marco task 称为 task
  1. console.log('script start') // 1
  2. async function async1() {
  3. await async2()// 去下一个 让出来
  4. console.log('async1 end')//7
  5. }
  6. async function async2() {
  7. console.log('async2 end')//2
  8. }
  9. async1()
  10. setTimeout(function() {
  11. console.log('setTimeout')//9
  12. }, 0)
  13. new Promise(resolve => {
  14. console.log('Promise') //3
  15. resolve()
  16. })
  17. .then(function() {
  18. console.log('promise1')//5
  19. })
  20. .then(function() {
  21. console.log('promise2')//6
  22. })
  23. console.log('script end') //4

观察这段代码。1-4 5-9
1-2牢记promise await是回调。
2-3-4 顺序执行 promise
然后继续执行 promise的回调 5-6
7 promise任务结束,继续回调
9 settimeout

总结:

  • 先执行同步代码,这是宏观任务。
  • 同步代码结束,当前执行队列空,看异步代码
  • 执行微观任务
  • 渲染页面
  • 执行完微观任务,回到宏观任务执行settimeout等

所以,promise和 setTimeout 比,promise是微观任务先执行

微观任务 process.nextTick promise MutationObserver
宏观任务 script setTimeout setInterval setImmediate I/O UI Rendering

node中的 event loop

和浏览器完全不同
node分为六个阶段,反复执行

timers — pending callbacks — idle,prepare — poll — check — close callbacks

timer

执行 setTimeout setInterval

I/O

处理上一轮循环中少数未执行的I/O 回调

poll

  • 回调timer阶段
  • 执行 io 回调

慢慢看吧。


  • 高阶函数,异步问题
  • 发布订阅、观察者模式
  • Promise应用
  • 实现
  • 面试题
  • 扩展
  • generator和co库
  • async/await

https://promiseplus.com

高阶函数

这个函数式高阶函数

  • 一个函数的参数是一个函数,比如回调函数
  • 一个函数返回一个函数

应用场景

扩展业务代码。

假设我们有一个代码想要拓展

  1. // 默认参数
  2. function say(){console.log('say')}
  3. // 补充before方法
  4. Function.prototype.before = function(cb){
  5. return ()=>{
  6. cb()
  7. this()
  8. }
  9. }
  10. let beforeSay = say.before(()=>console.log('before say'))
  11. beforeSay()

柯里化 反柯里化

发布订阅 观察者

如果你看过 vue2 的源码,对下面的东西你会感兴趣
发布

  1. let event = {
  2. arr:[],
  3. on(fn){this.arr.push(fn)},
  4. emit(){
  5. this.arr.forEach(fn=>fn())
  6. }
  7. }

我们维护了一个数组,先订阅,再发布

观察者模式

观察者和被观察者,被观察者发生变化需要通知。
也需要收集和通知。存在关联

  1. // 小宝宝
  2. class Subject {
  3. constructor(name){
  4. this.name = name
  5. this.state = 'happy'
  6. this.observers = [] // 观察者
  7. }
  8. setState(state){
  9. this.state = state
  10. // 调用观察者的方法
  11. this.observers.forEach(o=>o.update(this))
  12. }
  13. attach(o){
  14. this.observers.push(o) // 订阅
  15. }
  16. }
  17. var baby = new Subject('name')
  18. class Observer{
  19. constructor(name){
  20. this.name=name
  21. }
  22. update(instance){
  23. // 在这里就拿到了观察者的实例
  24. }
  25. } // 父母
  26. var father = new Observer('father')
  27. var mother = new Observer('mother')
  28. baby.attach(father)
  29. baby.attach(mother)
  30. baby.stetState('cry')

和vue太像了,以后用来举例子。

Promise/A+ 标准被社区支持。Promise 是一种状态机,只会是三种方式的一种。

-1 参考链接

  • 高级程序设计 第四版