zoukankan      html  css  js  c++  java
  • 连续多步骤业务流程的暂停、中断和恢复

    背景

    用户点击预订按钮,会进入预订流程,可以将其粗略的总结为 4 个步骤:1.表单验证;2.乘机人校验;3.创建订单;4.订单校验。

    其业务特点是:
    1)以时间为序顺序执行;
    2)阻塞执行,即前一步骤执行完毕后,才能执行后一步骤;
    3)任一步骤出错,流程终止。

    本项目基于 Vue.js 进行开发

    实现 version-1

    有了上面的背景铺垫,我们很容易实现预订逻辑,伪代码如下:

    async function verifyFormData() {}
    async function verifyPassengers() {}
    async function createBookOrder() {}
    async function verifyBookOrder() {}
    
    async function tapBookSubmit() {
      await verifyFormData()
      await verifyPassengers()
      await createBookOrder()
      await verifyBookOrder()
      ...
    }
    

    简洁明了,非常棒! but,新的需求很快就来了。

    新的需求

    从实际的体验中,我们发现预订的时间太长,除了傻傻的等待,什么也做不了,这能忍?所以我们决定从两个方面进行优化:一方面要求后台加快响应速度,另一方面则是优化交互。自然,落在前端头上的任务,就是优化交互流程了。

    需求:预订 loading 弹窗增加关闭按钮,用户点击关闭,弹出二次确认框(含继续、取消两个按钮),点击继续则继续流程,点击取消则终止本次预订(如果创单成功,还需调用取消订单接口取消订单),用户未操作,则等待。

    实现 version-2

    因为新增需求并不影响原来的逻辑,只是对它们进行了一次增强,这其实是装饰器干的活儿。然而现阶段要使用装饰器,还得额外引入新的编译流程,杀鸡焉用牛刀?索性就用高阶函数改造一下:

    async function bookDecorator(fn) {
      return async (...args) => {
        if (this.BookingModal.isPaused) {
          // TODO: 暂停超过3min钟,结束本次预订
          await sleep(0.8);
          return bookingDecorator(fn)(...args);
        }
        if (this.BookingModal.isAborted) {
          // 取消订单
          if (this.__canCancelOrder) {
            await cancelOrder();
          }
          throw "用户取消了本次预订";
        }
        return fn.call(this, ...args);
      }
    }
    
    function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms))
    }
    
    async function tapBookSubmit() {
      await bookDecorator(verifyFormData)()
      await bookDecorator(verifyPassengers)()
      await bookDecorator(createBookOrder)()
      await bookDecorator(verifyBookOrder)()
      ...
    }
    

    仅更改了少量代码,我们就完成了新的需求,感觉还不错~

    实现 version-3

    version-2 的实现有两个瑕疵:1)tapBookSubmit 函数中有重复代码;2)bookDecorator 及内部的递归每次都会创建新的函数,有爆栈的风险。对此,我们进行一点优化

    async function runSequence(arr) {
      let count = 0
      let res = null
    
      while(true) {
        if (!arr[count]) return res
    
        if (this.BookingModal.isPaused) {
          await sleep(0.8);
        } else if (this.BookingModal.isAborted) {
          // 取消订单
          if (this.__canCancelOrder) {
            await cancelOrder();
          }
          throw "用户取消了本次预订";
        } else {
          res = await arr[count++]()
        }
      }
    }
    
    async function tapBookSubmit() {
      const res = await runSequence([
        verifyFormData, 
        verifyPassengers, 
        createBookOrder, 
        verifyBookOrder
      ])
      ...
    }
    

    version-3 也有自己的问题——如何给列表中的函数传参?

  • 相关阅读:
    15 手写数字识别-小数据集
    14 深度学习-卷积
    5.线性回归算法
    9、主成分分析
    8、特征选择
    4.K均值算法--应用
    6.逻辑回归
    12.朴素贝叶斯-垃圾邮件分类
    13、垃圾邮件2
    大数据应用期末总评
  • 原文地址:https://www.cnblogs.com/fayin/p/13474626.html
Copyright © 2011-2022 走看看