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

异步解决方案----PromiseandAwait

时间:2023-04-02 12:13:32 HTML

前言异步编程模式在前端开发过程中越来越重要。从最初的XHR到封装后的Ajax,都是在尝试解决异步编程过程中的问题。随着新的ES6标准的到来,处理异步数据流也有了新的解决方案。我们都知道,在传统的ajax请求中,当异步请求之间的数据之间存在依赖关系时,可能会产生丑陋的多层回调,俗称'回调地狱'(callbackhell),令人望而生畏。Promise的出现让我们告别回调函数,写出更优雅的异步代码。在实践的过程中,我发现Promise并不完美。Async/Await是近年来添加到JavaScript中的最具革命性的特性之一。Async/Await提供了另一种方法使异步代码看起来像同步代码。接下来我们介绍这两种处理异步编程的方案。一、Promise的原理和基本语法1、Promise的原理Promise是一种对异步操作的封装,可以通过一个独立的接口加入,在异步操作成功或失败时执行方法。主流规范是Promises/A+。Promise中有几种状态:pending:初始状态,未完成或被拒绝;fulfilled:运行成功,为了表述方便,将fulfilled换成resolved;拒绝:操作失败。Pending可以转换为fulfilled或rejected,并且只能转换一次,也就是说,如果pending转换为fulfilled状态,则不能再转换为rejected。而fulfilled和rejected状态只能由pending转化而来,两者不能相互转化。2.Promise的基本语法Promise实例必须实现方法then()必须能够接收两个函数作为参数then()必须返回一个Promise实例//如果低版本浏览器不支持Promise,通过cdn这种方式2.Promise多个串行操作Promise可以做更多的事情。比如有几个异步任务,需要先完成任务1。如果成功,则去执行任务2,如果有任何任务失败,则不会继续执行错误处理函数。要串行执行这样的异步任务,没有Promise,就需要写一层层嵌套的代码。使用Promises,我们只需要编写job1.then(job2).then(job3).catch(handleError);其中job1、job2和job3都是Promise对象。例如,我们想在加载第一张图片后加载第二张图片。如果其中一个执行失败,执行报错函数:varsrc1='https://www.imooc.com/static/img/index/logo_new.png'varresult1=loadImg(src1)//result1isaPromiseobjectvarsrc2='https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'varresult2=loadImg(src2)//result2是一个Promise对象result1.then(function(img1){console.log('第一张图片加载完成',img1.width)returnresult2//链式操作}).then(function(img2){console.log('第二张图片加载完成',img2.width)}).catch(function(ex){console.log(ex)})这里需要注意的是,同一个promise可以多次调用then方法,并且then方法必须返回一个promise对象。上面的例子中,如果result1.then没有返回一个明文的Promise实例,则默认为自己的Promise实例,即result1,result1.then返回一个result2实例,然后执行.then实际上执行的是result2。然后。三、Promise除string外的常用方法Promise除了可以连续执行多个异步任务外,还可以并行执行异步任务。想象一个页面聊天系统。我们需要从两个不同的URL获取用户的个人信息和好友列表。这两个任务可以并行执行。使用Promise.all()实现以下功能:varp1=newPromise(function(resolve,reject){setTimeout(resolve,500,'P1');});varp2=newPromise(function(resolve,reject){setTimeout(resolve,600,'P2');});//当执行p1和p2时,当它们都完成时then:Promise.all([p1,p2]).then(function(results){console.log(results);//得到一个数组:['P1','P2']});有时,多个异步任务是为了容错。比如同时从两个URL中读取用户的个人信息,只需要先拿到返回的结果即可。在这种情况下,使用Promise.race():varp1=newPromise(function(resolve,reject){setTimeout(resolve,500,'P1');});varp2=newPromise(function(resolve,reject){setTimeout(resolve,600,'P2');});Promise.race([p1,p2]).then(function(result){console.log(result);//'P1'});由于p1执行得更快,Promise的then()将得到结果'P1'。p2继续执行,但是执行结果会被丢弃。总结:Promise.all接受一个promise对象数组,全部完成后,统一执行success;Promise.race接受一个包含多个promise对象的数组,只要有一个完成,就执行成功接下来我们对上面的例子做如下修改,加深对两者的理解:varsrc1='https://www.imooc.com/static/img/index/logo_new.png'varresult1=loadImg(src1)varsrc2='https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'varresult2=loadImg(src2)Promise.all([result1,result2]).then(function(datas){console.log('all',datas[0])//console.log('all',datas[1])//})承诺.race([result1,result2]).then(function(data){console.log('race',data)//})如果我们组合使用Promise,我们可以将很多异步任务以并行和串行的方式组合起来执行四、Async/Await介绍和使用异步操作在JavaScript编程中是一件比较麻烦的事情。许多人认为asyncfunction是异步操作的最终解决方案。一、Async/Await简介async/await是一种新的异步代码编写方式,优于回调函数和Promise。async/await是基于Promise实现的,不能用于普通的回调函数。async/await和Promise一样,是非阻塞的。async/await使异步代码看起来像同步代码,不再有回调函数。但是改变不了JS的单线程和异步的本质。2、Async/Await的用法使用await,函数必须标明async。await后面是一个需要安装babel-polyfill的Promise实例。安装完记得引入//npmi--save-devbabel-polyfillfunctionloadImg(src){constpromise=newPromise(function(resolve,reject){constimg=document.createElement('img')img.onload=function(){resolve(img)}img.onerror=function(){reject('图片加载失败')}img.src=src})returnpromise}constsrc1='https://www.imooc.com/static/img/index/logo_new.png'constsrc2='https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'constload=asyncfunction(){constresult1=awaitloadImg(src1)console.log(result1)constresult2=awaitloadImg(src2)console.log(result2)}load()函数执行时,一旦遇到await,会先返回,等待触发的异步操作完成,然后在函数体中执行下面的语句。5.Async/Await错误处理await命令背后的Promise对象可能会被拒绝,所以最好将await命令放在try...catch代码块中。try..catch错误处理更符合我们平时写同步代码时处理的逻辑。异步函数myFunction(){try{awaitsomethingThatReturnsAPromise();}catch(err){console.log(err);}}6.为什么Async/Await更好?Async/Await相对于Promise有很多优点,下面介绍其中三个:1.Async/Await的简单使用显然可以节省大量代码。不需要写.then,不需要写匿名函数来处理Promise的resolve值,不需要定义冗余的数据变量,也避免了代码嵌套。2.中间值你可能遇到过这样的场景,调用promise1,用promise1返回的结果调用promise2,再用两者的结果调用promise3。您的代码很可能如下所示:constmakeRequest=()=>{returnpromise1().then(value1=>{returnpromise2(value1).then(value2=>{returnpromise3(value1,value2)})})}使用async/await,代码会变得非常简单直观constmakeRequest=async()=>{constvalue1=awaitpromise1()constvalue2=awaitpromise2(value1)returnpromise3(value1,value2)}3.在下面的条件语句的例子中,需要获取数据,然后根据返回的数据决定是直接返回还是继续获取??更多的数据。constmakeRequest=()=>{returngetJSON().then(data=>{if(data.needsAnotherRequest){returnmakeAnotherRequest(data).then(moreData=>{console.log(moreData)returnmoreData})}else{console.log(data)returndata}})}代码嵌套(6层)可读性较差,它们只是传达了最终结果需要传递给最外层Promise的意思。使用async/await编写可以大大提高可读性:console.log(moreData)returnmoreData}else{console.log(data)returndata}}如果觉得文章对你有帮助,请点赞关注我的GitHub博客,非常感谢!参考文章Async/AwaitinsteadofPromisePromiseandAwait/Async前端异步解决方案廖雪峰的Javascript教程[[翻译]Promises/A+规范]的6个理由(http://www.ituring.com.cn/art...async函数的含义和用法