当前位置: 首页 > Web前端 > HTML5

async-await

时间:2023-04-05 18:50:49 HTML5

我们上次讲了Promise,今天继续讲async/await这组API。async/await是ES7标准,Promise是ES6标准,async/await的API集也是用来帮助我们写异步代码的。它建立在Promise之上,有点像Okhttp和Retrofit的关系。什么是异步?异步函数myFirstAsyncFunction(){try{constfulfilledValue=awaitdoSomeThing();}catch(rejectedValue){//...}}看起来像上面那样。async一般不会单独使用,而是和await一起使用,一个async函数内部可能有零个或多个await。即使你没有学过Promise,这段代码也很容易理解。这就是异步函数的优势所在。调用异步函数时,它会立即返回一个Promise。awaitawait不能单独使用,在非async函数内部调用会报错。await通常后面跟一个Promise,也可以是其他东西,比如一个值,或者一个变量,或者一个函数。如果await后面没有Promise,则将返回已解决的Promise。当async函数执行到await时,会挂起整个async函数的执行过程,放弃自己的控制权,只有在等待的基于Promise的异步操作完成或被拒绝后,进程才会恢复。当然,一个async函数也会返回一个Promise,也就是说await后面也可以跟一个async函数。async/awaitAPI集感觉就像Kotlin中的协程和挂起函数。为什么要使用异步?隐藏Promise,更容易理解假设我们要请求一个接口,然后打印出响应数据,捕获异常。使用Promise大致是这样写的:err=>{console.error('fetchfailed',err);});}如果是用async函数写的,会是这个样子:asyncfunctionlogFetch(url){try{constresponse=awaitfetch(url);控制台.log(等待响应.text());}catch(err){console.log('获取失败',err);}}虽然代码行数差不多,但是代码看起来更简洁,比嵌套少了很多。请求一个接口数据,然后打印,可以看到,很简单。就像刚才的例子,如果你没有学过Promise,并不影响你阅读和理解异步代码的能力。这看起来没什么,但是对于一个大型项目来说,人员的技术水平参差不齐,不能保证每个人都熟悉和了解Promise。我们要做的是尽可能的降低代码的理解难度,让大多数人都能看懂。理解。用同步的思路写异步逻辑async/await最大的好处就是我们可以用同步的思路写异步业务逻辑,所以整体代码看起来更容易理解。让我们举个例子。上面的代码还是比较简单的,我们再举一个复杂一点的例子。我们想要获得网络资源的大小。如果我们使用Promise,它可能看起来像这样:).then(functionprocessResult(result){if(result.done)returntotal;constvalue=result.value;total+=value.length;console.log('Receivedchunk',value);returnreader.read().then(processResult);})});}即使你学过Promise,这段代码也不容易看懂,何况没学过Promise的同学。因为中间有一个循环过程,异步执行过程并不认为我们之前学的一个链式调用可以解决。当然你也可以提取出来让代码更简洁。constprocessResult=(result)=>{如果(result.done)返回总计;constvalue=result.value;总计+=值.长度;console.log('接收到数据块',value);returnreader.read().then(processResult);}functiongetResponseSize(url){returnfetch(url).then(response=>{constreader=response.body.getReader();lettotal=0;返回阅读器。read().then(processResult)});}但是还有一个方法,我们就强制吧。但是让我们看看如何处理异步函数。异步函数getResponseSize(url){constresponse=awaitfetch(url);constreader=response.body.getReader();让result=awaitreader.read();让总计=0;while(!result.done){constvalue=result.value;总计+=值.长度;console.log('接收到数据块',value);//获取下一个结果result=awaitreader.read();}returntotal;}OHHHHHHHHH这样是不是看起来更流畅了?因为await表达式会阻塞执行,甚至直接阻塞循环,所以整体看起来是同步的代码更加直观易懂。当心await阻塞由于await可以阻塞async函数的执行,代码看起来更像同步代码,更容易阅读和理解。但是要注意await阻塞,因为有些阻塞是不必要的,使用不当可能会影响代码的性能。如果我们想将网络数据与本地数据结合起来,错误的示例可能如下所示:asyncfunctioncombineData(url,file){letnetworkData=awaitfetch(url)letfileData=awaitreadFile(file)console.log(networkData+fileData)}实际上,我们不必等到一个文件被读完才去读下一个文件。我们可以把两个文件一起读,读完后再合并,这样可以提高代码的运行速度。我们可以这样写:asyncfunctioncombineData(url,file){letfetchPromise=fetch(url)letreadFilePromise=readFile(file)letnetworkData=awaitfetchPromiseletfileData=awaitreadFilePromiseconsole.log(networkData+fileData)}在这个case,可以同时进行网络请求和读取文件,这样可以节省很多时间。这主要是利用了Promise一旦创建就立即执行的特性。不懂的同学可以复习上一节的内容。当然,如果你熟悉Promise,可以直接用Promise.all来处理,或者await后面跟着Promise.all,这里就不多说了。异常处理try...catch在async函数中,异常处理一般是try...catch。如果不执行try...catch,一旦await表达式reject,async函数返回的Promise就会reject。其实结合Promise,如果一个Promise状态最终确定为rej??ect,而后面的then没有传递给reject函数,或者没有catch,那么就会抛出异常。从这个角度来看,用try...catch将await表达式包裹在async函数中,可能就是为了捕获异常,将reject信息传递给catch。这里没有例子。