本文转载自微信公众号《零后程序员小三》,作者003,转载本文请联系零后程序员小三公众号。什么是异步编程?异步编程让我们可以执行一个长期的任务,程序可以不等到任务完成就继续执行后面的代码,然后以回调函数(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");如果还有其他回调,这个就更了可怕,继续写,谁都能变的心慌。我们称之为回调地狱SolvingCallbackHell为了解决这个回调地狱,Promise诞生了。我们在页面中动态更新数据,也就是AJAX技术,是使用Promise的APIfetch()实现的。我们可以尝试使用fetch()来获取接口的数据。运行之后我们可以知道它返回了一个Promise对象,但是我们还没有得到我们想要的数据,因为Promise的翻译就是承诺的意思,所以他应该会在后面为我们实现我们想要的需求,所以我最后加一个then,然后翻译成andthen的意思就是传入它的then方法,传入一个回调函数。如果后面请求成功,则调用回调函数,将请求的函数作为参数传递给fetch("http://jsonplaceholder.typicode.com/posts/1").then((response)=>...)但是如果是这样的话,Promise和回调函数就没有区别了。不过Promise的好处是可以用链式结构把多个异步操作串联起来,也就是后面的response.json()方法也会返回一个Promise,然后就是把返回的response转换成以后转成json格式,fetch("http://jsonplaceholder.typicode.com/posts/1").then((response)=>response.json())然后我们就可以继续添加我们想要的操作了perform,然后直接走,比如打印出结果或者把结果存到容器里fetch("http://jsonplaceholder.typicode.com/posts/1").then((response)=>response.json()).then((json)=>console.log(json))Promise的链式调用避免了代码的层层嵌套。虽然有很长的链式调用,但代码只是向下增长而不是向右增长。可读性会大大提高。但是在使用异步操作的时候,也会遇到错误,比如各种网络问题,数据格式不正确等。然后我们可以通过在最后添加一个catch()来捕获这些错误。如果前面任何一个阶段发生错误,就会触发catch,然后后面的then就不再执行同步编程中的这个try/used了。与catch块类似,Promise也提供了一个finally方法,该方法将在Promise链结束后调用。不管有没有错误,我们都可以在这里做函数清理。毕竟,必须有开始和结束。async/await现在让我们来看看async/await。简单的说,它是基于Promise的一个语法糖,可以让异步操作变得更简单、更清晰。具体步骤是先使用async将返回值为Promise对象的函数标记为异步函数,就像刚才使用的fetch()是异步函数一样,在异步函数中可以调用其他异步函数,但是而不是使用then,而使用更简单的await。中文意思是等待,等待,所以await会等待Promise完成,直接返回最终结果。使用了async/await之后,函数就变成了异步函数,我们可以直接得到我们想要的结果,所以有些已经是服务端返回的响应数据了,然后我们就可以进行响应操作了。await虽然看起来是暂停了函数的执行,但是在等待的过程中也可以处理其他的任务。比如我把返回的数据转成json数据,因为await底层是基于Promise和事件循环机制实现的,具体操作还有很多要自己去尝试。这样,我们就得到了我们想要的数据。但是在使用的时候也要注意await的错误用法。例如,asyncfunctionfn(){constsome1=awaitfetch("http://jsonplaceholder.typicode.com/posts/1")constsome2=awaitfetch("http://jsonplaceholder.typicode.com/posts/2")constsome3=awaitfetch("http://jsonplaceholder.typicode.com/posts/3")...}fn()虽然没什么问题,但是这样写会破坏两个fetch()操作的并行性,因为我们等到第一个任务完成后再执行第二个任务,然后执行下面的代码。所以我们有一个小技巧。就是把所有的Promises和Promise.all结合起来,然后去await,比如我下面的方法asyncfunctionfn(){constsome1=awaitfetch("http://jsonplaceholder.typicode.com/posts/1")constsome2=awaitfetch("http://jsonplaceholder.typicode.com/posts/1")constsome3=awaitfetch("http://jsonplaceholder.typicode.com/posts/1")const[a,b,c]=awaitPromise.all([some1,some2,some3])}fn()会大大提高程序的运行效率。最后,我们不能直接在全局或普通函数中使用await关键字。await只在异步中有效。如果我们要在最外层使用await,需要先定义一个异步函数,然后在函数体中使用,使用asyncawait可以写出更清晰易懂的异步代码,不再需要使用底层的Promiseobject,包括then()、catch()等函数,如果老版本的浏览器不支持asyncawait语法,可以编译成同样兼容老版本的代码
