点击关注“八戒技术团队”,阅读更多技术干货
异步与并发,是前端程序员在日常编程中难以绕开的话题,本文介绍一个用于封装异步操作与管理并发的Vue插件:Vue-Concurrency。
Vue-Concurrency旨在为异步操作的执行和取消提供合理的抽象,它减少了样板代码,提供了可靠的派生状态,并为节流、反跳、轮询等技术提供了新的方法。
先看看它是怎么使用的:
前端开发中常用到的实现异步操作的方式是使用Promise和Async函数,为什么要使用Task来定义异步操作呢?
原因是Promise和Async函数都存在一个痛点:无法取消。比如,Async函数一旦开始执行,就会一直执行下去,无法从外部中止它。相比之下,Task的最大特点就是它支持取消。如下图:
cancelAll,可以从当前运行到的yield中断点中止任务的运行,被取消后,后面的代码都不会运行。异步操作的生命周期管理
下面这个"10秒倒计时"的例子直观地演示了这个问题:
该如何解决这个问题呢?
这里我们引入现代编程语言中的结构化并发(Structured Concurrency)的概念,使用结构化并发的思路来解决面临的问题。
结构化并发类似于我们从所有现代编程语言中知道的结构化编程。结构化编程防止了代码的随机跳转,而是将程序结构为一组嵌套的代码块。块的生命周期永远不会重叠。如果块B嵌套在块A中,则B的生存期不能超过A的生存期。同样,结构化并发防止线程A启动的线程B的生存期超过A的生存期:
(图片来源: https://250bpm.com/blog:71/)
将结构化并发的思路引入到前端开发中来,就需要对异步操作的生命周期进行管理。一般来说,异步操作的生存期不能超过它所在的组件,也就是说,如果一个组件被移除了,由该组件触发的所有未执行完成的异步操作都应该被中止掉。
Vue-Concurrency 中的Task就是按照这个思路设计的,在组件移除前所有正在运行的Task会被自动中止。
使用Task来实现"10秒倒计时"的例子:
组件被移除前会中止所有未完成的Task的运行,这是由 Vue-Concurrency 自动完成的,不需要书写额外的代码。这保证了异步操作的生命周期不超过它所在组件的生命周期,避免了资源浪费和内存泄露。
并发管理
前面讨论了异步操作的生命期管理,下面我们讨论异步操作的并发问题。
默认情况下,多次调用myTask.perform(),任务将并发运行。例如:
myTask.perform();myTask.perform();
myTask的两个实例将同时运行(除非它们所在的组件被销毁,在这种情况下它们将被取消)。
默认情况下,每次执行perform()后,都启动一个新的任务实例,它们同时执行,互不干涉。
通常情况下,我们希望一个任务在同一时间只有一个实例在运行。例如,有一个将页面数据保存到服务器的异步任务,我们不希望该任务同时运行——我们希望它按顺序运行(排队依次运行),或者如果该任务已经在运行了,需要忽略该任务的后续调用(防重复提交)。
通过代码手动来实现这些限制枯燥且繁琐,这也是导致出现冗余的、容易出错的样板代码的根源。Vue-Concurrency 提供的并发管理能力可以很方便的实现对并发的控制,并且不会引入额外的冗余代码。
指定并发策略
drop
将Task的并发策略设置为drop意味着:在Task的运行过程中,如果再次调用它,将丢弃新的调用。drop一般会用在表单防重复提交、防止重复请求的场景。
restartable
enqueue
进入队列,依次运行。如果Task正在运行,后续的调用都进入队列等待,任务按入队顺序依次执行。
keepLatest
最近的调用进入队列等待,丢弃其它的调用。如果Task在运行过程中,又触发了多次新的调用,keepLatest策略会入对最近一次调用,同时将前面的调用丢弃。
并发管理示例
指定最大并发量
maxConcurrency与drop示例:
maxConcurrency与enqueue示例:
派生状态
Task提供的主要派生状态如下,在Vue
组件的JS和模板代码里都能直接调用:
1.isRunning: 当至少有一个任务实例正在运行时为true,否则为false;
2.isIdle: 没有任务实例正在运行时为true,否则为false;
3.performCount:任务已执行的次数;
4.concurrency:当前正在运行的任务实例数。如果使用drop/enqueue/restartable指定了并发策略(不指定maxConcurrency),则该数字永远不会大于1,此属性对于调试非常有用;
5.state:任务状态的字符串描述,取值为“running”或“idle”,用于调试;
6.last:最近运行的任务实例;
7.lastSuccessful:最近运行完成的任务实例(任务成功运行完成,没有报错或异常),lastSuccessful.value可以得到异步操作的返回值;
8.lastErrored: 最近运行失败的任务实例(任务由于报错或异常运行失败),通过lastErrored.error可以得到具体的报错信息。
总结
(扫码查看演示文稿和更多示例)
希望以上内容能对有需要的人有所帮助。
前往“发现”-“看一看”浏览“朋友在看”