当前位置: 首页 > 科技观察

Vue3,用组合写出更好的代码:AsyncWithoutAwaitpattern

时间:2023-03-12 19:58:09 科技观察

如果能让异步代码正常工作,就可以大大简化我们的代码。然而,处理这种额外的复杂性,尤其是使用Compatible时,可能会令人困惑。这篇文章介绍了没有等待的异步模式。这是一种在组合中编写异步代码的方法,没有通常令人头疼的问题。无需等待的异步使用组合API编写异步行为有时会很麻烦。所有异步代码必须在设置函数的末尾出现在任何反应代码之后。如果你不这样做,它会干扰你的反应。当设置函数到达await语句时,它将返回。一旦它返回,组件就会被挂载,应用程序将照常执行。在await之后定义的任何反应性,无论是计算的、观察者的还是其他的,都还没有被初始化。这意味着在await之后定义的计算属性首先不会被模板使用。相反,它仅在异步代码完成并且设置函数完成执行后才存在。然而,有一种方法可以编写异步组件,可以在任何地方使用而没有这些麻烦。constcount=ref(0);//这种异步数据获取不会干扰我们的响应能力const{state}=useAsyncState(fetchData());constdoubleCount=computed(()=>count*2);实现没有await的异步模式,为了实现这个模式,我们将同步挂起所有的反应值。每当异步代码完成时,这些值将被异步更新。首先,我们需要准备好我们的状态并返回它。我们将使用null值进行初始化,因为我们还不知道该值是什么。导出默认useMyAsyncComposable(promise){conststate=ref(null);returnstate;}其次,我们创建一个等待我们承诺的方法,然后将结果设置为状态:constexecute=async()=>{state.value=awaitpromise;}每当这个promise返回时,它就会主动更新我们的状态。现在我们只需要将这个方法添加到组合中。导出默认useMyAsyncComposable(promise){conststate=ref(null);//添加执行方法...constexecute=async()=>{state.价值=等待承诺;}//...并执行它!执行();returnstate;}我们在从useMyAsyncComposable方法返回之前调用了执行函数。但是,我们没有使用await关键字。当我们在execute方法中停止并等待promise时,执行流程立即返回到useMyAsyncComposable函数。然后它继续执行execute()语句并从可组合项返回。导出默认useMyAsyncComposable(promise){conststate=ref(null);constexecute=async()=>{//2.等待promise执行完成state.value=awaitpromise//5.一段时间后...//Promise完成,状态更新//execute已执行}//1.执行`execute`方法execute();//3.await将控制返回到这一点。//4.返回状态并继续执行“setup”方法returnstate;}promise在后台执行,因为我们没有等待它,所以它不会中断setup函数中的流程。我们可以将这个可组合项放置在任何地方,而不会影响响应能力。让我们看看VueUse中的一些组合是如何实现这种模式的。useAsyncStateuseAsyncState允许我们在任何地方执行任何异步方法并获得响应更新的结果。const{state,isLoading}=useAsyncState(fetchData());查看源代码时,您可以看到它实现了这个模式,但具有更多功能和更好地处理边缘情况。这是useAsyncState的简化版本:exportfunctionuseAsyncState(promise,initialState){conststate=ref(initialState);constisReady=ref(false);constisLoading=ref(false);常量错误=参考(未定义);异步函数执行(){error.value=undefined;isReady.value=false;isLoading.value=true;尝试{常量数据=等待承诺;state.value=数据;isReady.value=true;}catch(e){错误.value=e;}isLoading.value=false;}执行();return{state,isReady,isLoading,error,};}这个可组合系统还返回isReady,它告诉我们数据何时被提取。我们还得到isLoading和error来跟踪我们的加载和错误状态。现在来看另一个可组合项,我认为它有一个令人着迷的实现。如果向useAsyncQueue传递一组承诺函数给useAsyncQueue,它将按顺序执行每个函数。因此,在开始下一个任务之前,它会等待上一个任务完成。为了更加灵活,将其上一个任务的结果作为输入传递给下一个任务。const{result}=useAsyncQueue([getFirstPromise,getSecondPromise]);下面是官网的例子:constgetFirstPromise=()=>{//创建我们的第一个promisereturnnewPromise((resolve)=>{setTimeout(()=>{resolve(1000);},10);});};constgetSecondPromise=(result)=>{returnnewPromise((resolve)=>{setTimeout(()=>{resolve(1000+result);},20);});};const{activeIndex,result}=useAsyncQueue([getFirstPromise,getSecondPromise]);即使是异步执行代码,我们也不需要使用await。可组合程序甚至在内部也不使用await。相反,我们“在后台”执行这些承诺,并让结果被动更新。让我们看看这种组合是如何工作的。//初始化一些默认值constinitialResult=Array.from(newArray(tasks.length),()=>({state:promiseState.pending,data:null,});//将默认值改为responsiveconstresult=reactive(initialResult);//声明一个响应式下标constactiveIndex=ref(-1);主要功能由一个reduce支持,它一个一个处理每个功能tasks.reduce((prev,curr)=>{returnprev.then((prevRes)=>{if(result[activeIndex.value]?.state===promiseState.rejected&&interrupt){onFinished();return;}returncurr(prevRes).then((currentRes)=>{updateResult(promiseState.fulfilled,currentRes);activeIndex.value===tasks.length-1&&onFinished();returncurrentRes;})}).catch((e)=>{updateResult(promiseState.rejected,e);onError();returne;})},Promise.resolve());Reduce方法有点复杂,我们拆开来看一下:tasks.reduce((prev,curr)=>{//...},Promise.resolve());然后,开始处理每个任务。这是通过在先前的承诺之上链接一个.then来完成的。如果承诺被拒绝,则提前中止并返回。if(result[activeIndex.value]?.state===promiseState.rejected&&interrupt){onFinished();return;}如果没有提前终止,执行下一个任务并传递上一个promise的结果。我们还调用updateResult方法将其添加到组合返回的结果数组中1&&onFinished();返回currentRes;});如您所见,此可组合项实现了AsyncWithoutAwait模式,但该模式只是整个可组合项的几行代码。所以它不需要很多额外的工作,只要记住把它放到位就可以了总结如果我们使用AsyncWithoutAwait模式,我们可以更轻松地使用异步组合。这种模式允许我们将异步代码放在我们想要的地方,而不用担心破坏响应能力。作者:MichaelThiessen译者:小智来源:vuemastery原文:https://img.ydisp.cn/news/20220901/rgcpo3mtdd4