Async函数是一个了不起的东西,Chrome55将默认支持它们。它允许你编写基于promise的代码,但它看起来像同步代码,不会阻塞主线程。因此,它使您的异步代码看起来不那么“聪明”,但更具可读性。异步函数的代码示例:asyncfunctionmyFirstAsyncFunction(){try{constfulfilledValue=awaitpromise;}catch(rejectedValue){//...}}如果在函数声明前使用async关键字,则可以在该函数中使用它等待。当您等待承诺时,该功能将以非阻塞方式暂停,直到承诺得到解决。如果Promise返回成功状态,你将获得返回值,如果返回状态失败,则抛出失败消息。提示:如果您不熟悉承诺,请查看我们的承诺指南。示例1:打印响应假设我们要请求一个URL并打印出响应。这是一个使用承诺的示例代码:functionlogFetch(url){returnfetch(url).then(response=>response.text()).then(text=>{console.log(text);}).catch(err=>{console.error('fetchfailed',err);});}下面使用异步函数实现相同的功能:asyncfunctionlogFetch(url){try{constresponse=awaitfetch(url);console.log(awaitresponse.text());}catch(err){console.log('fetchfailed',err);}}可以看到代码行数和上面的例子一样,但是async函数的使用方式让所有回调函数消失了!这使得我们的代码非常容易阅读,特别是对于那些不是特别熟悉promises的同学。提示:您await的任何值都通过Promise.resolve()传递,因此您可以安全地使用非本地promise.Async函数的返回值无论您是否在函数内部使用await,Async函数总是返回一个promise。当async函数返回任何值时,返回的promise将调用resolve方法。当async函数抛出异常错误时,返回的promise会调用reject方法,所以://waitmsmillisecondsfunctionwait(ms){returnnewPromise(r=>setTimeout(r,ms));}asyncfunctionhello(){awaitwait(500);return'world';}hello()执行时,返回成功状态,传入值为world的promise.asyncfunctionfoo(){awaitwait(500);throwError('bar');}hello()执行时,返回失败状态,传递的值是Error('bar')的承诺。示例2:更复杂情况下的响应流程其中,async函数更能体现其优越性。假设我们要记录chunks数据并将其转化为响应流,并返回最终的消息长度。提示:“记录块”让我感到尴尬。以下是如何使用promise:if(result.done)returntotal;constvalue=result.value;total+=value.length;console.log('Receivedchunk',value);returnreader.read().then(processResult);})});}你看,我向“地下”杰克阿奇博尔德保证。看看我是如何在其中调用processResult并创建一个异步循环的?写那个让我觉得“聪明”。但就像大多数“聪明”的代码一样,你必须盯着它看很长时间才能弄清楚它在做什么,就像九十年代的那些魔眼图片一样。引用让我们用异步函数重写上面的函数:asyncfunctiongetResponseSize(url){constresponse=awaitfetch(url);constreader=response.body.getReader();letresult=awaitreader.read();lettotal=0;while(!result.done){constvalue=result.value;total+=value.length;console.log('Receivedchunk',value);//getthenextresultresult=awaitreader.read();}returntotal;}所有“智能”代码都消失了。现在新的异步循环使用了可靠的、看起来很简单的while循环,这让我觉得非常整洁。更重要的是,在未来,我们将使用异步迭代器,它将使用for循环而不是while循环,这样会更整洁!提示:我喜欢流。如果你不熟悉流,你可以查看我的指南异步函数的其他语法我们已经看到如何使用asyncfunction(){},但async关键字也可以用于其他函数语法。箭头函数//mapsomeURLstojson-promisesconstjsonPromises=urls.map(asyncurl=>{constresponse=awaitfetch(url);returnresponse.json();});提示:array.map(func)不关心你是否给它一个异步function,它只会将其视为返回承诺的普通函数。因此,第二次回调的执行并没有等待第一次回调中的await处理完成。对象方法conststorage={asyncgetAvatar(name){constcache=awaitcaches.open('avatars');returncache.match(`/avatars/${name}.jpg`);}};storage.getAvatar('jaffathecake')。然后(...);类方法classStorage{constructor(){this.cachePromise=caches.open('avatars');}asyncgetAvatar(name){constcache=awaitthis.cachePromise;returncache.match(`/avatars/${name}.jpg`);}}conststorage=newStorage();storage.getAvatar('jaffathecake').then(…);提示:类的构造函数和getters/settings不能是异步函数。警告!避免过分强调顺序即使您编写的代码看起来是同步的,也要确保您不会错过并行处理的机会。asyncfunctionseries(){awaitwait(500);awaitwait(500);return"done!";}上面的代码需要1000毫秒才能完成,但是:asyncfunctionparallel(){constwait1=wait(500);constwait2=wait(500);awaitwait1;awaitwait2;return"done!";}上面的代码只用了500毫秒,因为两个等待是同时处理的。示例3:按顺序打印请求消息假设我们想要获得一系列URL响应消息,并尽快按正确的顺序打印出来。深吸一口气....下面是使用promise实现的代码:;});//logtheminordertextPromises.reduce((chain,textPromise)=>{returnchain.then(()=>textPromise).then(text=>console.log(text));},Promise.resolve());}是的,这就达到了目的。我正在使用reduce作为承诺链,我太“聪明”了。这是一段如此“聪明”的代码,但我们****不这样做。但是,当上面的代码转换为使用asyncfunctions时,它看起来太有序了,以至于会混淆我们::-1:notrecommended-toomajorizedfororderasyncfunctionlogInOrder(urls){for(consturlofurls){constresponse=awaitfetch(网址);console.log(awaitresponse.text());}}看起来整洁多了,但我的第二个请求只会在第一个请求完全处理后发出,等等。这比上面的承诺示例慢得多。幸运的是,还有一个中立的解决方案::+1:推荐-漂亮且并行的异步函数logInOrder(urls){//fetchalltheURLsinparallelconsttextPromises=urls.map(asyncurl=>{constresponse=awaitfetch(url);returnresponse.text();});//logtheminsequencefor(consttextPromiseoftextPromises){console.log(awaittextPromise);}}在这个例子中,所有的url都被一个一个地请求和处理,但是'smart'reduce是标准的,通用的并且更易读的forloop循环。浏览器兼容性和解决方法在撰写本文时,Chrome55已经默认支持异步功能。但在所有主流浏览器中,它仍在开发中:Edge-在build14342+背后的一个标志Firefox-主动开发Safari-主动开发解决方案1:生成器所有主流浏览器的最新版本都支持生成器,如果你正在使用它们,你可以稍微填充异步函数。Babel正是为你做的,这里有一个通过BabelREPL编写的例子——转译后的代码感觉很熟悉。这种转换机制是Babel的es2017预设的一部分。提示:BabelREPL非常有趣,试试吧。我建议你现在就这样做,因为当你的目标浏览器支持异步功能时,你只需要从你的项目中删除Babel。但如果你真的不想使用转译器,你可以使用Babel的polyfill进行预览。asyncfunctionslowEcho(val){awaitwait(1000);returnval;}当你使用上面提到的polyfill并点击预览时,你可以将上面的代码替换为:constslowEcho=createAsyncFunction(function*(val){yieldwait(1000);returnval;});请注意,您将一个生成器(function*)传递给createAsyncFunction,并使用yield而不是await。除此之外,它们的工作原理相同。解决方案二:regenerator如果你想兼容老浏览器,babel也可以转换生成器,这样你就可以在IE8以上的浏览器中使用async函数,但是你需要使用babel的es2017preset和es2015preset,你会看到转换后的代码不好看,请注意代码臃肿。异步所有的东西!一旦所有浏览器都支持异步函数,请对所有返回承诺的函数使用异步!因为它不仅使您的代码更简洁,而且还确保异步函数始终返回一个promise。早在2014年,我就对异步函数的出现感到非常兴奋,现在很高兴看到浏览器支持它们。哎呀!
