轻松驾驭异步定时:Vue3的useIntervalAsync

前言

我们经常会遇到需要定时执行某些操作,特别是那些需要异步处理的操作。而useIntervalAsync这个钩子函数就像是你的贴心小助手,帮助你轻松驾驭这些异步定时任务。

代码

封装成一个hooks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
TS复制代码import { onUnmounted, ref } from 'vue';
// import { TimeUnit, toMilliseconds } from '@tmp/utils';

export type Cleanup = () => any;
export type CallbackReturn = void | Cleanup;
export type Callback = (...args: any[]) => CallbackReturn | Promise<CallbackReturn>;
// unit: TimeUnit = 'millisecond'
export const useIntervalAsync = (callback: Callback, delay: number) => {
const timeout = ref<number | null>(null);
const canceled = ref<boolean>(false);
const cleanup = ref<Cleanup | void>(); // 将延迟时间转换为毫秒

// delay = toMilliseconds(delay, unit);

const run: TimerHandler = async () => {
if (canceled.value) {
return;
} // 清理之前的回调函数
if (typeof cleanup.value === 'function') {
cleanup.value();
} // 执行回调函数并获取清理函数
cleanup.value = await Promise.resolve(callback()); // 设置下一次任务轮询的定时器
timeout.value = globalThis.setTimeout(run, delay);
}; // 初始化任务轮询

run(); // 刷新任务轮询,取消当前定时器,重新执行回调函数

const flush = () => {
// eslint-disable-next-line no-unused-expressions
timeout.value && globalThis.clearTimeout(timeout.value);
run();
}; // 取消任务轮询,清理定时器和回调函数

const cancel = () => {
// eslint-disable-next-line no-unused-expressions
timeout.value && globalThis.clearTimeout(timeout.value);
canceled.value = true;
if (typeof cleanup.value === 'function') {
cleanup.value();
}
}; // 恢复任务轮询,重新启动定时器

const recover = () => {
canceled.value = false;
flush();
}; // 在组件卸载时取消任务轮询

onUnmounted(() => {
cancel();
});

return {
flush,
cancel,
recover,
};
};

export default useIntervalAsync;

解析

一、了解useIntervalAsync*\

useIntervalAsync接收两个参数:一个回调函数和一个延迟时间。回调函数是你希望在每次定时器触发时执行的异步操作,而延迟时间则决定了这个操作执行的频率。

二、内部工作原理

  1. 引用变量useIntervalAsync内部使用了Vue 3的ref函数来创建响应式引用,包括timeout(存储定时器的ID)、canceled(表示定时器是否已取消)和cleanup(存储清理函数)。
  2. 定时逻辑:在run函数中,首先检查canceled的值,如果为true,则直接返回,不再执行后续操作。然后,如果cleanup中有函数,则执行它进行清理工作。接着,执行回调函数并等待其完成(或等待其返回的Promise完成),将返回的清理函数(如果有的话)赋值给cleanup,并设置下一次定时器的触发时间。
  3. 初始化和刷新:在钩子函数被调用时,会立即执行一次run函数来初始化定时器。而flush函数则用于刷新定时器,它会先清除当前的定时器(如果存在),然后重新执行run函数来设置新的定时器。
  4. 取消和恢复cancel函数用于取消定时器,并将canceled设置为true,同时执行cleanup中的清理函数(如果有的话)。而recover函数则用于恢复定时器,它将canceled设置为false,并调用flush函数来刷新定时器。
  5. 组件卸载时的清理:通过onUnmounted钩子,确保在组件卸载时调用cancel函数来取消定时器,避免内存泄漏。

三、使用与优势

使用useIntervalAsync非常简单,只需传入回调函数和延迟时间即可。而且,由于它支持异步操作和清理函数,你可以更加灵活和高效地管理你的代码。无论是在数据获取、状态更新还是其他需要定时执行的异步操作中,useIntervalAsync都能帮助你轻松应对。

想象一下,useIntervalAsync就像一个训练有素的管家。你告诉它什么时候该做什么(回调函数和延迟时间),它就会准时为你完成任务。而且,它还很聪明,知道什么时候该出现(定时器触发时),什么时候该隐身(定时器取消时)。最重要的是,它还会帮你处理那些繁琐的清理工作,让你的代码保持整洁和高效

应用

场景:扫码登录

如我之前写的这篇文章:业务: 前后端实现二维码扫码登录-深度剖析

扫码登录应用场景与useIntervalAsync的结合

扫码登录是许多应用(如微信、支付宝、QQ等)中常见的一种登录方式。用户通过扫描屏幕上的二维码,快速、便捷地完成登录过程。而useIntervalAsync这个钩子函数,在这种场景下也可以发挥重要的作用。

扫码登录流程

  1. 生成二维码:用户在前端页面上触发扫码登录操作,后端生成一个唯一的二维码并返回给前端展示。
  2. 轮询检查登录状态:用户扫描二维码后,后端会验证二维码并等待用户的确认登录操作。此时,前端需要通过某种方式轮询检查登录状态。传统的轮询方式可能会使用setInterval,但这种方式不支持异步操作,并且不能很好地处理异步任务。而useIntervalAsync则完美解决了这个问题。

useIntervalAsync在扫码登录中的应用

  • 异步检查登录状态:在useIntervalAsync的回调函数中,你可以发起一个异步请求到后端,检查用户是否已经扫描二维码并确认登录。这个异步请求可能是基于fetchaxios等库。
  • 处理登录状态变化:一旦后端返回用户已经确认登录的信息,你可以在回调函数中处理这个状态变化,比如跳转到用户的主页或者显示登录成功的提示。
  • 清理定时器:当用户成功登录后,你需要停止轮询操作。在useIntervalAsync中,你可以通过调用cancel函数来清除定时器,确保不再发起不必要的请求。
  • 优雅的错误处理:如果在轮询过程中发生错误,比如网络问题或者后端服务不可用,你可以在useIntervalAsync的回调函数中捕获这些错误,并给用户展示相应的提示信息

扫码登录应用场景与useIntervalAsync的结合

扫码登录是许多应用(如微信、支付宝、QQ等)中常见的一种登录方式。用户通过扫描屏幕上的二维码,快速、便捷地完成登录过程。而useIntervalAsync这个钩子函数,在这种场景下也可以发挥重要的作用。

扫码登录流程

useIntervalAsync在扫码登录中的应用

  • 异步检查登录状态:在useIntervalAsync的回调函数中,你可以发起一个异步请求到后端,检查用户是否已经扫描二维码并确认登录。这个异步请求可能是基于fetchaxios等库。
  • 处理登录状态变化:一旦后端返回用户已经确认登录的信息,你可以在回调函数中处理这个状态变化,比如跳转到用户的主页或者显示登录成功的提示。
  • 清理定时器:当用户成功登录后,你需要停止轮询操作。在useIntervalAsync中,你可以通过调用cancel函数来清除定时器,确保不再发起不必要的请求。
  • 优雅的错误处理:如果在轮询过程中发生错误,比如网络问题或者后端服务不可用,你可以在useIntervalAsync的回调函数中捕获这些错误,并给用户展示相应的提示信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
ts复制代码import { ref, onUnmounted } from 'vue';
import useIntervalAsync from './useIntervalAsync'; // 假设useIntervalAsync已经定义好并可以导入

export default {
setup() {
const checking = ref(true); // 控制是否还在轮询检查状态
const isLoggedIn = ref(false); // 存储用户是否已登录的状态
const loginStatusError = ref(null); // 存储登录状态检查过程中出现的错误

// 模拟扫码登录的异步检查函数
const checkLoginStatus = async () => {
try {
// 发起异步请求到后端检查登录状态
const response = await fetch('/api/check-login-status');
const data = await response.json();

if (data.loggedIn) {
// 如果已登录,设置状态并停止轮询
isLoggedIn.value = true;
checking.value = false;
// 这里可以添加登录成功后的处理逻辑,如跳转到用户主页
} else {
// 如果未登录,继续轮询
checking.value = true;
}
} catch (error) {
// 处理请求错误
loginStatusError.value = error;
// 根据业务需要,可以选择是否停止轮询
checking.value = false;
}
};

// 使用useIntervalAsync设置轮询
const { cancel, flush } = useIntervalAsync(checkLoginStatus, 2000); // 每2秒检查一次登录状态

// 组件卸载时取消轮询
onUnmounted(() => {
cancel();
});

// 初始化轮询
if (checking.value) {
flush();
}

return {
isLoggedIn,
loginStatusError,
// 可以将cancel和flush暴露给模板,以便在需要时手动控制轮询
cancel,
flush
};
}
};

image.png

gh_db79ec2f6f73_258.jpg

在这个例子中,checkLoginStatus函数是一个异步函数,它模拟了向后端发起请求检查用户是否已经扫码并确认登录的过程。这个函数被useIntervalAsync定时调用,每隔2秒执行一次。

当isLoggedIn变为true时,表示用户已经成功登录,此时轮询将停止。如果在轮询过程中发生错误,loginStatusError将被设置,并且可以根据业务逻辑选择是否停止轮询。

在Vue组件的setup函数中,我们通过onUnmounted钩子确保在组件卸载时取消轮询,以避免不必要的资源消耗和潜在的错误。

最后,我们将isLoggedIn、loginStatusError、cancel和flush暴露给模板,这样我们就可以在模板中显示登录状态,或者在需要时手动控制轮询的开始和结束。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%