JS—异步、回调、高阶函数

并发与并行

  • 并发是指两个或多个事件链随时间发展交替执行,以至于从更高的层次来看,就像是同时运行(但在任意时刻只处理一个事件)
  • 并发的关键是你有处理多个任务的能力,不一定同时。
  • 并行的关键是你有同时处理多个任务的能力。
  • 并发和并行都可以是很多个线程,就看这些线程能不能同时被(多个)cpu执行,如果可以就说明是并行,而并发是多个线程被(一个)cpu 轮流切换着执行。
  • 区别:是否【同时】

并发与并行

  • 单线程事件循环是并发的一种形式
  • 并发的实现方式:多进程,多线程,事件循环
  • 我的理解是:并发是多个逻辑流交替执行,看起来像是同时运行,其实任意时刻只能处理一个;而并行是真正的同时执行。

同步与异步

  • 同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)
  • 同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。
  • 异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

阻塞与非阻塞

  • 阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态
  • 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
  • 非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
  • 我的理解:异步阻塞是没有必要的;ajax请求最好不要设置为同步,chrome浏览器会警告

阻塞与非阻塞,同步与异步知乎链接
阻塞与非阻塞与是否同步异步无关

事件循环

  • 机制:while(true)每轮循环取出事件队列中的队头事件执行,事件队列(先进先出)。
  • setTimeout()是设定一个定时器,当定时器到时后,环境会把回调函数放到事件循环中。如果此时事件队列中有多个事件,那么该回调就会等待,所以setTimeout定时器的精度可能不高,只能确保回调函数不会在指定的时间间隔之前运行。
  • AJAX请求:JS程序发出ajax请求,从服务器端获取数据,并设置了回调函数。发出请求的时候,JS引擎会通知宿主环境,让其完成了网络请求,拿到数据后执行该回调函数。然后,浏览器会设置侦听来自网络的响应,拿到数据后,将该回调函数插入到事件循环。
  • 竞态条件:函数执行顺序的不确定性(来自于插入事件队列顺序的不确定性)
  • 竞态条件可能会导致结果的不确定性,所以需要通过协调交互顺序来解决这种情况。
  • 并发协作:JS中我的理解是:将大任务分解为多个小任务,分别利用异步机制插入到事件队列中,例如可利用setTimeout(f,0)将小任务插入到事件队列中,如果事件队列目前有待执行的事件就可以先执行,以此来避免大任务阻塞事件队列中其他任务的执行。

JavaScript单线程与异步

  • why单线程:作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的问题。
  • why异步:我的理解:浏览器应用决定了会有很多网络请求,会有很多不可控的速度因素,浏览器是直接和用户交互,用户交互体验很重要,所以不能阻塞,但同步非阻塞处理起来会很麻烦,因为同步需要编写代码去检测任务是否执行成功。所以就用异步,异步是通信机制决定了调用在发出之后就可以直接返回。
  • 所有同步任务执行完毕,系统就会循环读取事件队列。
  • 阮一峰的链接:http://www.ruanyifeng.com/blo...
  • 深入理解事件循环
  • 深入事件循环

回调

  • 回调是JS中最基础的异步模式
  • 回调地狱:1)难以编写,大脑的思维方式不适应面向回调的异步代码。2)为了处理可能出现的各种情况,只能在回调的每个步骤中硬编码,造成代码的复杂与重复,难以重用和维护。
  • 信任问题:控制反转,即把自己程序的一部分执行控制交给了某个第三方。

高阶函数

  • JS作为一门完整的面向对象的编程语言,借鉴了函数式语言的特性,比如闭包、高阶函数等。
  • 高阶函数: 函数可作为参数被传递,函数可作为返回值输出。
  • 函数作为参数被传递:

1)回调函数,即将变化部分的代码封装为函数,将函数作为参数传递。本来可以将变化的部分抽离出来,形成两个函数,但实际上这两个函数有依赖关系,所以只能参数调用,放到同一个函数中执行。
2)Array.prototype.sort 可以传入函数作为排序规则。

异步控制台

  • JS调试最好使用断点,因为console.log()是由宿主环境添加到JS中,控制台I/O处理可能会有延时。

《你不知道的javascript中卷》第二部分异步和性能 1.1 异步控制台部分:

并没有什么规范或一组需求指定console.* 方法族如何工作——它们并不是JavaScript 正式
的一部分,而是由宿主环境(请参考本书的“类型和语法”部分)添加到JavaScript 中的。因此,不同的浏览器和JavaScript 环境可以按照自己的意愿来实现,有时候这会引起混淆。

尤其要提出的是,在某些条件下,某些浏览器的console.log(..) 并不会把传入的内容立即输出。出现这种情况的主要原因是,在许多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(从页面/UI 的角度来说)浏览器在后台异步处理控制台I/O 能够提高性能,这时用户甚至可能根本意识不到其发生。

例如在mousemove监听函数中console.log,是不会打印出内容的(mousemove触发太频繁,而console.log属于I/O操作,浏览器为了性能考虑,会将这种频繁I/O放到后台运行)。

解决方案: 如果断点调试不方便的话,可以将需要打印的内容,挂载到window对象,mousemove完成后再打印出来看。

更多博客: https://github.com/Lmagic16/blog

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章