js层面事务回滚的理解

文章介绍了在Vue应用中实现点赞功能时,如何使用事务管理确保点赞操作的原子性,包括亮起状态、请求后端接口、成功或失败时的回滚策略以及回滚时恢复上一次状态的方法。
摘要由CSDN通过智能技术生成

最近有一个这样的需求:


点赞功能:点了赞之后要先亮起来,然后再去请求后端的接口,1、成功了不变!2、失败了要回滚到之前的状态。之前一直有听说后端有事务回滚(没去了解过实现原理,但应该就是出问题了,回到上一个状态吧!)

实现的代码如下:

useTransaction.ts

interface UseTransactionParams<T> {
  initialValue?: T[];
  maxLength?: number;
  // 回滚回调方法
  rollbackCallback?: (item: T) => void;
}
interface UseTransactionReturn<T> {
  commit: (item: T) => void;
  rollback: () => void;
  getLast: () => T;
}
/**
 * 事务回滚
 * 【业务场景】点赞
 * 【流程】先亮,若失败则回滚
 */
export function useTransaction<T>(
  params?: UseTransactionParams<T>
): UseTransactionReturn<T> {
  let list: T[] = [];
  const initialValue = params?.initialValue || [];
  const maxLength = Math.abs(params?.maxLength || 2);
  const rollbackCallback = params?.rollbackCallback;
  if (initialValue.length) {
    list.push(...initialValue);
  }
  const updateList = () => {
    list = list.slice(-maxLength);
  };
  updateList();
  const getLast = () => {
    return list[list.length - 1];
  };
  const commit = (item: T) => {
    list.push(item);
    updateList();
  };
  const rollback = () => {
    rollbackCallback && rollbackCallback(getLast());
  };
  return {
    // 提交事务
    commit,
    // 回滚
    rollback,
    // 获得最近的数据
    getLast,
  };
}

使用:

<template>
  <LikeAndUnlike
    :like="like"
    :unlike="unlike"
    :loading="loading"
    @like="handleLike"
    @unlike="handleUnLike"
    @update="handleUpdateStatusDebounce"
  />
</template>

<script lang="ts" setup>
import LikeAndUnlike from "./LikeAndUnlike.vue";
import type {
  FeedBackCreatePopupParams,
  GetChatListApiItemDTO,
} from "@/api/chat/types";
import { ref, computed } from "vue";
import { debounce } from "lodash-es";
import { showToast } from "vant";
import { useTransaction } from "@/hooks/web/useTransaction";

const props = defineProps<{
  item: GetChatListApiItemDTO;
  isLastMsg?: boolean;
}>();
type Loop = () => void;
const emit = defineEmits<{
  (
    event: "delete-feed-back",
    item: GetChatListApiItemDTO,
    done: Loop,
    success: Loop,
    fail: Loop
  ): void;
  (
    event: "update-feed-back",
    item: GetChatListApiItemDTO,
    params: FeedBackCreatePopupParams,
    done: Loop,
    success: Loop,
    fail: Loop
  ): void;
}>();

const loading = ref(false);
const like = ref<boolean>(props.item.feedCode === "1");
const unlike = ref<boolean>(props.item.feedCode === "0");
const likeStatus = computed(() => {
  if (like.value) {
    return "1"; // 喜欢
  }
  if (unlike.value) {
    return "0"; // 不喜欢
  }
  return "delete"; // 两个都没选
});
const handleLike = (value: boolean) => {
  like.value = value;
};
const handleUnLike = (value: boolean) => {
  unlike.value = value;
};
const transaction = useTransaction<[boolean, boolean]>({
  // 初始事务
  initialValue: [[like.value, unlike.value]],
  // 回滚事务时触发的方法
  rollbackCallback([prevLike, prevUnLike]) {
    like.value = prevLike;
    unlike.value = prevUnLike;
  },
});
/**
 * 校验值的必要性
 * fixbug: 来回切换触发重复 删除/创建
 */
const checkNecessity = (nowLike: boolean, nowUnLike: boolean) => {
  const [prevLike, preUnLike] = transaction.getLast();
  if (prevLike === nowLike && preUnLike === nowUnLike) {
    throw Error(`没必要的提交,请求的新值和旧值相同!`);
  }
};
const handleUpdateStatus = (
  params: Partial<FeedBackCreatePopupParams>,
  needCheck = true
) => {
  needCheck && checkNecessity(like.value, unlike.value);
  loading.value = true;
  const feedCode = likeStatus.value;
  const done = () => (loading.value = false);
  const success = () => {
    transaction.commit([like.value, unlike.value]); // 推送事务
  };
  const fail = () => {
    showToast("操作失败");
    transaction.rollback(); // 事务回滚
  };
  if (feedCode === "delete") {
    emit("delete-feed-back", props.item, done, success, fail);
    return;
  }
  params.feedCode = feedCode;
  const newSuccess = () => {
    success();
    if (params.feedCode === "0" && (params.feedType || params.feedContent)) {
      // 如果是踩 且 填写了踩的内容
      showToast("反馈成功");
    }
  };
  emit(
    "update-feed-back",
    props.item,
    params as FeedBackCreatePopupParams,
    done,
    newSuccess,
    fail
  );
};
/**
 * 防止用户疯狂 取消/确定
 */
const handleUpdateStatusDebounce = debounce(handleUpdateStatus, 500);
</script>

核心就是:初始化时记录回滚方法。然后成功时记录这次成功的值,如果失败就执行回滚,那就会去执行我们初始化的方法并传入上一次成功的值,用于回滚!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值