Promise, async, await 学习

异步编程简介:

介绍:异步编程是一种编程范式,旨在提高程序的性能和响应能力。该模型允许程序在执行某些任务时,不必等待这些任务完成就可以进行下一步操作,从而提高了程序的效率。

作用:异步编程通常用于涉及网络请求、文件读写、数据库查询等I/O密集型操作,以及高计算量的任务。在传统的同步编程中,这些操作会阻塞程序的执行,导致程序变慢或失去响应。而使用异步编程可以使这些I/O操作并行执行,从而提高程序的性能和响应能力。


并发 Concurrency、 并行 Parallelism、异步 Asynchronous、同步 Synchronous 的区别

(1)并发 Sancufrericy :代表计算机能够同时执行多项任务

Promise, async, await 学习_第1张图片

计算机怎么做到并发 Concurrency 

(1)对于单核处理器,计算机可以通过分配时间片的方式——让一个任务运行一段时间,然后切换另外一个任务,再运行一段时间,不同的任务会这样交替往复的一直执行下去。——>这个过程也被称作是进程或者线程的上下文切换(context switching)

Promise, async, await 学习_第2张图片

(2)对于多核处理器,可以在不同的核心上真正并行地执行任务,而不用通过分配时间片的方式运行——> 并行(parallelism)

Promise, async, await 学习_第3张图片


同步和异步是两种不同的编程模型

(1)"同步” Synchronous 代表需要等到必须前一个任务执行完毕之后,才能进行下一个任务。因此在同步中并没有并发或者并行的概念。

Promise, async, await 学习_第4张图片

(2)异步 Asynchronous 则代表不同的任务之间并不会相互等待、先后执行,即执行任务A时,也可以同时执行任务B

Promise, async, await 学习_第5张图片

一个典型实现异步的方式则是通过多线程编程

多线程 Multithreoding : 创建多个线程并且启动他们,在多核的环境下,每个线程就会被分配到独立的核心上运行,实现真正的并行。

如果使用单核心处理器,或者通过设置亲和力(AfFnty) 强制将线程绑定到某个核心上

Promise, async, await 学习_第6张图片

操作系统则会通过分配时间片的方式来执行这些线程,不过这些线程依然是在“并发”地执行Promise, async, await 学习_第7张图片Javascript 本身是没有多线程的概念的,不过通过它的函数回调(function callback)机制,依然能够做到单线程的“并发”。

例:通过 fetch() 函数 同时访问多个网络资源

Promise, async, await 学习_第8张图片

⭕注意:

虽然主程序和回调函数看起来是同时进行的,但它们依然是运行在同一个线程中。

Promise, async, await 学习_第9张图片

Promise, async, await 学习_第10张图片

多线程编程、单线程的异步编程如何选择

(1)单线程的异步编程:对于I / O 密集的应用程序,比如 web 应用就会经常执行网络操作,数据库访问,这类应用就非常适合使用异步编程的方式。

Promise, async, await 学习_第11张图片

如果是使用多线程的方式,则可能浪费系统资源(如下图)

因为每个线程的绝大多数时间都是在等待这些 I / O 操作,而线程自身会占用额外的内存(线程内存开销,线程切换开销还有线程资源竞争问题)

(2)多线程编程:

多线程编程则非常适合于计算量密集的应用,如视频图像处理,科学计算等。它能够让每一个 CPU 核心发挥最大的功效,而不是消耗在空闲的等待上。


JavaScript 中有两种实现异步的方式

(1)回调函数  Callback Function

Promise, async, await 学习_第12张图片

可以使用 setTimeout() 让一个函数在指定的时间后执行,这个函数本身会立刻返回,程序紧接着会执行之后的代码,而传入的回调函数则会等到预定的时间才会执行

⭕注意:

  1. 这里的()=>是箭头表达式,相当于函数定义的简化写法
  2. Javascript 从设计之初就是一个单线程的编程语言 

即便看上去这里的回调函数和主程序在并发执行,但它们都运行在同一个主线程中。实际上主线程中还运行了我们写的其它代码,包括界面逻辑、网络请求、数据处理等等等等。虽然只有单个线程在执行,但这种单线程的异步编程方式其实有诸多优点。

由于所有操作都运行在同一个线程中,因此我们无须考虑线程同步或者资源竞争的问题,并且从源头上避免了线程之间的频繁切换。从而降低线程自身的开销。

Promise, async, await 学习_第13张图片

缺点:

回调函数虽然简单好理解,但有一个明显的缺点:需要依次执行多个异步操作,会变成回调地狱 Callback Hell ——整个程序会一层接着一层的嵌套下去,可读性会非常差。

Promise, async, await 学习_第14张图片


(2)Promise

Promise 就是为了解决这个问题——请求会在未来某个时刻返回数据,随后可以调用它的然后方法并传递一个回调函数。

Promise的API中的fetch就是很好的例子:Promise, async, await 学习_第15张图片

fetch 用来发起一个请求来获取服务器数据,可以用它动态更新页面的内容(即AJAX 技术 Asynchronous JavaScript and XML)

如果请求成功完成,则回调函数会被调起,请求的结果也会以参数的形式传递进来

Promise, async, await 学习_第16张图片

而且  Promise的优点在于它可以用一种链式结构将多个异步操作串联起来!——链式调用 Chaining

response.json() 方法 也会返回一个 Promise,代表在未来某个时刻,将返回的数据转换成 JSON 格式。

如果想要等到它完成之后再执行其它的操作,可以在后面追加一个 then,然后执行接下来的代码。

Promise, async, await 学习_第17张图片

Promise的链式调用避免了代码的层层嵌套,即便有很长的链,代码也不过是向下方增长而并非向右,因此可读性会提升不少。

使用异步操作也会遇到各种错误:各种网络问题或者返回的数据格式不正确等等

Promise, async, await 学习_第18张图片

错误处理  Error Handling

如果想捕获这些错误,最简单的方法是附加一个 catch 在链式结构的末尾。

Promise, async, await 学习_第19张图片

如果之前任意一个阶段发生了错误,那么将触发 catch ,之后的 then 将不会执行。这和同步编程中用到的 try/catch 块很类似。

Promise, async, await 学习_第20张图片

类似的Promise还提供finally 方法,会在Promise 链结束之后调用。无论失败与否,都可以在这里做清理工作。

例如:如果我们用到了加载动画,则可以在这里关闭他。


新标准ECMA17 中加入的两个关键字 async、await

它们是基于 Promise之上的一个语法糖,使异步操作更加的简单。

首先我们需要使用 async 关键字将函数标记为异步函数。

异步函数就是指返回值为 Promise 对象的函数,比如之前用到的 fetch() 就是一个异步函数。Promise, async, await 学习_第21张图片

在异步函数中我们可以调用其它的异步函数,不过不再需要使用 then(),而是使用一个更加简洁的 await 语法。await 会等待 Promise 完成之后直接返回最终的结果。所以这里的 response 已经是服务器返回的响应数据了。

Promise, async, await 学习_第22张图片

注意:await 虽然看上去会暂停函数的执行,但在等待的过程中,JavaScript 同样可以处理其它的任务,比如说更新界面,运行其他的代码等等。因为 await 底层是基于 Promise 和事件循环机制实现的。


await 使用时的陷阱

(1)分别去 await 这两个异步操作,虽然不存在逻辑错误,但这样写会打破这两个 fetch() 操作的并行。因为会等到第一个任务执行完成之后才开始执行第二个任务。

Promise, async, await 学习_第23张图片

因此更高效的做法是将所有 Promise 用 Promise.all 组合起来。

Promise, async, await 学习_第24张图片

 


(2)如果需要在循环中执行异步操作,是不能够直接调用 forEach 或者 map 这一类方法的,尽管我们在回调函数中写了 await,但这里的 forEach 会立刻返回,它并不会暂停等到所有异步操作都执行完毕

Promise, async, await 学习_第25张图片 如果我们希望等待循环中的异步操作都一一完成之后才继续执行,那我们还是应当使用传统的 for 循环。

Promise, async, await 学习_第26张图片

更进一步,如果我们想要循环中的所有操作都并发执行 ——> for await

Promise, async, await 学习_第27张图片

这里的 for 循环依然会等到所有的异步操作都完成之后才继续向后执行 。


(3)不能在全局或者普通函数中直接使用 await 关键字,await 只能被用在异步函数(asyncfunction)中。

Promise, async, await 学习_第28张图片

如果我们想在最外层中使用 await,那么需要先定义一个异步函数,然后在函数体中使用它。

Promise, async, await 学习_第29张图片

使用async和await 可以让我们写出更清晰、更容易理解的异步代码,因此我们几乎不再需要使用底层的Promise 对象

Promise, async, await 学习_第30张图片

包括调用它的then() ,catch()函数等等。

即便是对于某些旧版本的浏览器(例如IE)不支持async语法 ,我们还是可以使用转译器将它们编译成旧版本也兼容的等效代码。

Promise, async, await 学习_第31张图片

你可能感兴趣的:(学习,javascript,前端)