文件上传三 React Filer 思想的一次实践

这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

上一篇文章我们使用了浏览器的 worker 方法,今天我们在来尝试用另外一种方法来获取文件的 hash

这个方法得益于 ReactFiber, 关于 Fiber 的原理我们在这里先不做过多的说明,就是把一个耗时比较长的任务切分成小片,每一个小片完成之后把任务之后就交还给 React 控制权,没其他优先级高的任务,就继续执行切分小片的任务。

关于 React Filber,我们在说明一点,它有用到 requestIdleCallback 这个方法,显而易见,根据名字我们就知道它是一个回调函数,就是在浏览器空闲时间可以使用的一个方法,但是它的兼容性不太好,還是一个实验性的功能。

关于 requestIdleCallback api 文档点这里

window.requestIdleCallback()方法插入一个函数,这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。

根据这个思想我们也来折腾一下我们的文件上传

  1. 我们先来定义一个需要执行的函数
  2. 当切片没有上传完毕且浏览器有空闲时间我们就执行切片任务
  3. 计算进度条

我们還是先利用 sparkMD5 来产生一个 buffer 的数组,每执行一个小片的任务就通过 appendToSpark 方法放在这个数组里面

const spark = new sparkMD5.ArrayBuffer()

这种方法的进度条可能就没有之前的那么精准了,这里是用 当前完成的小片任务数/切片总数 得到的,每个任务执行的 chunk 越大,进度条的精确度越小

这篇文章主要是开拓一下视野,当我们了解到了一些好的思想(Filber)看自己去能不能搞点有意思的东西实践一下。

下面附上完整的代码

export const calculateHashIdle = async (chunks: Array<chunksType>, hashProgress: any) => {
  return new Promise((resolve) => {
    const spark = new sparkMD5.ArrayBuffer()
    let count = 0
    const appendToSpark = async (file: any) => {
      return new Promise((resolve): void => {
        const reader = new FileReader()
        reader.readAsArrayBuffer(file)
        reader.onload = (e) => {
          spark.append(e.target.result)
          resolve()
        }
      })
    }

    const workLoop = async (deadline) => {
      // timeRemaining 获取当前帧的剩余时间
      while (count < chunks.length && deadline.timeRemaining() > 1) {
        // 空闲时间,且有任务
        await appendToSpark(chunks[count].file)
        count++
        if (count < chunks.length) {
          // 进度条
          hashProgress.value = Number(((100 * count) / chunks.length).toFixed(2))
        } else {
          // 小片任务执行完毕进度条设置为 100
          hashProgress.value = 100
          resolve(spark.end())
        }
      }
      window.requestIdleCallback(workLoop)
    }
    window.requestIdleCallback(workLoop)
  })
}
复制代码

补充说明:

  1. deadline 有两个参数
  • timeRemaining(): 当前帧还剩下多少时间
  • didTimeout: 是否超时
  1. requestIdleCallback 的缺陷

requestIdleCallback is called only 20 times per second - Chrome on my 6x2 core Linux machine, it's not really useful for UI work。—— from Releasing Suspense

这段话的意思是 requestIdleCallback 的 FPS 只有 20, 而页面的 fps 是 60(即一帧为 16.7 s),这远远低于页面流畅度的要求!

猜你喜欢

转载自juejin.im/post/7055096097311555621