快点上车,前端异步编程发车了

开发 前端
异步编程允许我们在执行一个长时间任务的时候,程序不用进行等待就可以继续执行后面的代码,直到任务完成后再以回调函数(callback)的方式回头通知你,这种编程模式避免了程序的阻塞,提高了效率,它适用于那些网络请求或者数据库操作的应用。

本文转载自微信公众号「零零后程序员小三」,作者003 。转载本文请联系零零后程序员小三公众号。

什么是异步编程?

异步编程允许我们在执行一个长时间任务的时候,程序不用进行等待就可以继续执行后面的代码,直到任务完成后再以回调函数(callback)的方式回头通知你

这种编程模式避免了程序的阻塞,提高了效率,它适用于那些网络请求或者数据库操作的应用。

实现异步的方式

回调函数,是最简单的实现异步的方式

  1. console.log('111'); 
  2.  setTimeout(() => { 
  3.      console.log("222"); 
  4.         }, 2000) 
  5.         console.log('333'); 

虽然,按照html文档的输出规则他是自上而下,但是在中间加了一个定时器,然后浏览器识别到了它,会马上执行,然后执行后面的代码,等到了给定时间,会以回调函数的方式返回。

在JS的设计之初,他一开始就是单线程的编程语言,尽管这里回调函数看上去和主线程一起进行的,但是都运行在一个线程中,况且主线程还运行其他代码。

虽然JS只有一个线程,但是还是有比较不错的优点的。因为所有的操作都在一个线程之中,所以不用去考虑资源竞争的问题,而且在源头就避开了线程之间的频繁切换,从而降低了线程开销

但是它也有一个致命的缺点,如果我们需要进行多个异步操作,我们可能会写出下面的代码

  1. console.log("111"); 
  2.  
  3.        setTimeout(() => { 
  4.            console.log("三秒后执行1"); 
  5.            setTimeout(() => { 
  6.                console.log("三秒后执行2"); 
  7.                setTimeout(() => { 
  8.                    console.log("三秒后执行3"); 
  9.                    setTimeout(() => { 
  10.                        console.log("三秒后执行4"); 
  11.                    }, 3000) 
  12.                }, 3000); 
  13.            }, 3000); 
  14.        }, 3000); 
  15.  
  16.        console.log("333"); 

如果再有别的回调,这样会更恐怖,一直写下去,换谁都看得心慌。我们管这个叫回调地狱

解决回调地狱

为了解决这个回调地狱,Promise诞生了。

我们在页面中动态的更新数据,也就是AJAX技术,就是使用Promise的API fetch()实现的

我们可以试一试用fetch()获取一个接口的数据

通过运行可知道他返回的是一个Promise对象,但是我们还没有获得我们想要的数据,因为Promise翻译一下就是承诺的意思,所以,他应该会在后来给我们实现我们想要的需求,所以,我在后面加个then,then翻译一下就也是然后的意思

所以就是传入它的then方法并传入一个回调函数,如果在后来这个请求成功之后,然后回调函数会被调起,请求的函数会被作为一个参数传入

  1. fetch("http://jsonplaceholder.typicode.com/posts/1"
  2. .then((response)=> ...) 

但是如果这样看来Promise和回调函数就没有区别了。

但是,Promise的优点在于它可以用一种链式结构将多个异步操作串联起来

也就是 比如下面的response.json()方法也会返回一个Promise,然后then之后就是将未来返回的response转换为json格式,

  1. fetch("http://jsonplaceholder.typicode.com/posts/1"
  2. .then((response)=>response.json()) 

然后我们还可以继续追加我们想要进行的操作,直接then下去,比如下面这样把结果打印出来或者把结果存到某个容器中等

  1. fetch("http://jsonplaceholder.typicode.com/posts/1"
  2. .then((response)=>response.json()) 
  3. .then((json) => console.log(json)) 

Promise的链式调用避免了代码层层嵌套,尽管有很长的链式调用,但也只是将代码向下方增长而不是向右。可读性会大大提高。

但是在使用异步操作的时候也会遇到错误,比如各种网络问题以及数据格式不正确等。然后我们可以通过在末尾添加一个catch()来捕获这些错误,如果之前任意一个阶段发生了错误,那么catch会被触发,然后之后的then将不会再执行

这跟同步编程中用到的try/catch块相似,Promise还提供了finally方法,会在Promise链结束后调用,无论是否出现错误,我们都可以在这里做函数清理的工作。毕竟要有首有尾嘛。

async/await

现在来看一下async/await,简单来说就是基于Promise之上的语法糖,可以让异步操作更加简单明了。

具体步骤就是

首先使用async将返回值为Promise对象的函数标记为异步函数,就像是刚刚用到的fetch()就是一个异步函数,在异步函数中可以调用其他异步函数,不过不是使用then,而是用更简单的await,中文意思就是等到,等待,所以await会等待Promise完成之后直接返回最终结果

用了async/await之后就是将函数变成异步函数,可以直接获取到我们想要的结果,所以some已经是服务器返回的响应数据了,然后我们就可以进行响应的操作了

await虽然看上去会暂停函数的执行,但是在等待的过程中同样可以处理其他任务,比如我这里将返回的数据转换为json数据,因为await底层就是基于Promise和事件循环机制实现的,具体操作还有很多自行去尝试。

这样我们就拿到了我们想要的数据了。

但是使用的时候也要留意await的错误用法

比如我这样

  1. async function fn() { 
  2.  
  3.             const some1 = await fetch("http://jsonplaceholder.typicode.com/posts/1"
  4.             const some2 = await fetch("http://jsonplaceholder.typicode.com/posts/2"
  5.             const some3 = await fetch("http://jsonplaceholder.typicode.com/posts/3"
  6.             ... 
  7.         } 
  8.  
  9.         fn() 

虽然看起来没有什么错误啊,但是这样写会打破这两个fetch()操作的并行,因为我们是等到第一个任务完成再执行第二个任务,然后再执行后面的代码。

所以我们有个小妙招。

就是将所有Promise用Promise.all组合起来,然后再去await,比如我下面的做法

  1. async function fn() { 
  2.  
  3.             const some1 = await fetch("http://jsonplaceholder.typicode.com/posts/1"
  4.             const some2 = await fetch("http://jsonplaceholder.typicode.com/posts/1"
  5.             const some3 = await fetch("http://jsonplaceholder.typicode.com/posts/1"
  6.              
  7.             const [a,b,c] = await Promise.all([some1,some2,some3]) 
  8.              
  9.         } 
  10.  
  11.         fn() 

这样的做法会让运行程序效率提升很多。

最后,我们不能在全局或者普通函数中直接使用await关键字,await只在异步中有效,如果我们想要在最外层中使用await那么需要先定义一个异步函数,然后再在函数体中使用它

使用async await可以写更清晰更容易的理解异步代码,而且不用再使用底层的Promise对象,包括then(),catch()函数等

如果旧版本浏览器不支持async await语法,可以通过转译器编译成旧版本也兼容的代码

 

责任编辑:武晓燕 来源: 零零后程序员小三
相关推荐

2021-04-09 09:51:52

CyclicBarri Java循环栅栏

2013-04-01 15:38:54

异步编程异步编程模型

2021-09-29 07:41:27

前端技术编程

2013-04-01 15:25:41

异步编程异步EMP

2020-10-15 13:29:57

javascript

2011-02-22 08:49:16

.NET同步异步

2011-02-22 09:09:21

.NETAsync CTP异步

2015-04-22 10:50:18

JavascriptJavascript异

2014-05-23 10:12:20

Javascript异步编程

2016-09-07 20:43:36

Javascript异步编程

2017-07-13 12:12:19

前端JavaScript异步编程

2013-08-20 15:54:14

异步编程windows编程

2021-06-02 09:01:19

JavaScript 前端异步编程

2021-06-06 16:56:49

异步编程Completable

2016-12-30 13:43:35

异步编程RxJava

2011-11-11 15:47:22

JavaScript

2022-07-08 14:14:04

并发编程异步编程

2021-03-22 08:45:30

异步编程Java

2015-09-16 15:11:58

C#异步编程

2017-05-05 08:44:24

PythonAsyncio异步编程
点赞
收藏

51CTO技术栈公众号