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

Posted 零零后程序员小三

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快点上车,前端异步编程发车了相关的知识,希望对你有一定的参考价值。

👨‍💻 博客主页:作者主页
👍 觉得文章不错的朋友可以点点赞和关注
🎁 粉丝福利:学习资料、简历模板统统都有点击领取

什么是异步编程?

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

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

实现异步的方式

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

 console.log('111');
 setTimeout(() => 
     console.log("222");
        , 2000)
        console.log('333');

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

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

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

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

 console.log("111");

        setTimeout(() => 
            console.log("三秒后执行1");
            setTimeout(() => 
                console.log("三秒后执行2");
                setTimeout(() => 
                    console.log("三秒后执行3");
                    setTimeout(() => 
                        console.log("三秒后执行4");
                    , 3000)
                , 3000);
            , 3000);
        , 3000);

        console.log("333");

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

解决回调地狱

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

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

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

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

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

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

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

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

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

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

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

fetch("http://jsonplaceholder.typicode.com/posts/1")
.then((response)=>response.json())
.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的错误用法

比如我这样

async function fn() 

            const some1 = await fetch("http://jsonplaceholder.typicode.com/posts/1")
            const some2 = await fetch("http://jsonplaceholder.typicode.com/posts/2")
            const some3 = await fetch("http://jsonplaceholder.typicode.com/posts/3")
            ...
        

        fn()

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

所以我们有个小妙招。

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

async function fn() 

            const some1 = await fetch("http://jsonplaceholder.typicode.com/posts/1")
            const some2 = await fetch("http://jsonplaceholder.typicode.com/posts/1")
            const some3 = await fetch("http://jsonplaceholder.typicode.com/posts/1")
           	
            const [a,b,c] = await Promise.all([some1,some2,some3])
            
        

        fn()

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

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

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

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

🤞作者小三是刚刚毕业不久全栈工程师,写的技术文章基本上是学习过程中笔记整理而来,大家看了之后如果喜欢可以给小弟点点赞哦。
🎁 粉丝福利:学习资料、简历模板统统都有点击领取

以上是关于快点上车,前端异步编程发车了的主要内容,如果未能解决你的问题,请参考以下文章

python爬虫--多任务异步协程, 快点,在快点......

老司机发车第八弹:前端跨终端适配和浏览器兼容

前端必学-函数式编程(六)

了解前端JavaScript中异步编程的发展

前端排序算法总结;前端面试题2.0;JavaScript异步编程

002 | 搭上SpringBoot事务源码分析专车