Skip to content

上传时序问题

问题背景

在使用 ElementPlus 的 Upload 组件进行自定义上传时,存在如下问题:

ts
/**
 * 上传与案件相关的资料,返回FileID
 * @param option
 * @param isChunk 是否使用分片上传
 */
export async function uploadCaseFile(
  option: UploadCaseFileOption,
  isChunk = false
) {
  // 文件大小超过50MB时使用分片上传
  if (isChunk && option.file.size > 50 * 1024 * 1024) {
    // 1. 分片上传
    const url = await uploadChunk(option); // 后端没有存URL
    // 2.绑定案件
    const fileID = await getFileIdAndBundleCaseByURL({
      url,
      caseId: option.data.caseId,
      type: option.data.type,
      fileName: option.file.name,
    });
    setTimeout(() => {
      option.onSuccess(fileID);
    }, 0);
  } else {
    console.log(option);
    const { process, source } = uploadNormalCaseFile(option, {
      caseId: option.data.caseId,
      type: option.data.type,
    });
    const res = await process;
    setTimeout(() => {
      option.onSuccess({
        ...res,
        source,
      });
    }, 0);
  }
}

如果没有 setTimeout,file.response 将为 undefined

setTimeout(fn, 0)

setTimeout(fn, 0) 的作用是将回调函数 fn 放入事件循环的任务队列中,使其在当前执行栈中的所有同步任务完成后立即执行。

尽管延迟时间为 0 毫秒,但它并不意味着立即执行。这是因为 JavaScript 是单线程的,它需要等待主线程空闲下来才能执行任务队列中的任务。

setTimeout(fn, 0) 的执行机制

  1. 调用 setTimeout(fn, 0):当调用 setTimeout 时,浏览器会启动一个计时器,并立即将回调函数 fn 放入任务队列(宏任务队列)。
  2. 执行同步代码:主线程继续执行当前执行栈中的所有同步代码。
  3. 事件循环:当同步代码执行完毕后,事件循环(Event Loop)开始工作。
  4. 执行回调函数:事件循环会从任务队列中取出 fn 并将其推入执行栈,从而执行回调函数。