【问题标题】:Resolving promises in JavaScript在 JavaScript 中解析 Promise
【发布时间】:2020-06-27 22:07:08
【问题描述】:

我很难获得按我需要的方式工作的承诺。我尝试了许多不同的方法来解决这个承诺,但我所做的一切都无法满足我的需要。我正在尝试在网页上拖放文件。我需要传递给调用以上传文件的 Set (this.files) 中所有文件的列表。问题是 Promise.all 是在 Promise 完成之前运行的。

我仍在努力将我的想法包裹在承诺上,所以也许我错了,但从我的所有研究看来,这应该可行。任何帮助将不胜感激。

  async dndDropFiles(event) {
    event.preventDefault();
    let ptable = [];
    this.files = new Set();
    if (event.dataTransfer.types[0] === "Files") {
      var items = event.dataTransfer.items;
      for (var i=0; i<items.length; i++) {
        // webkitGetAsEntry is where the magic happens
        var item = await items[i].webkitGetAsEntry();
        if (item) {
          ptable.push(new Promise(async resolve => {
            resolve(await this.dndTraverseFileTree(item, ""));
          }))
        }
      }  
      Promise.all(ptable)
        .then( (results) => {
          if (this.files.size > 0) {
            this.progress = this.uploadService.upload(this.files, this.currentDir, this.currentProject);
          }    
        })
    }
  }

  async dndTraverseFileTree(item, path) {
    if (item.isFile) {
      // Get file
      item.file((file) => {
        this.files.add(file);
      });
  } else if (item.isDirectory) {
      // Get folder contents
      let ptable = [];
      var dirReader = await item.createReader();
      dirReader.readEntries((entries) => {
        for (var i=0; i<entries.length; i++) {
          ptable.push(new Promise( async resolve => {
            resolve(await this.dndTraverseFileTree(entries[i], path + item.name + "/"));
          }));
        }
        Promise.all(ptable)
          .then (results => {});
      });
    }
  }

【问题讨论】:

  • 哇,什么微任务魔法:new Promise(async resolve1 =&gt; resolve(await new Promise(resolve2 =&gt; resolve2())) - 只是想知道你需要这个微任务魔法还是你的意思是(new Promise(r=&gt;r())).then(r=&gt;r)
  • 我在没有异步的情况下尝试过,但它也不起作用。你认为这会影响 Promise 的解决吗?
  • 问题是你有点混合了异步糖和承诺然后还创建了冗余承诺 - 我试一试并重写你的代码
  • 我从函数中提取了所有异步和等待,但我没有看到任何真正不同的地方。我仍然可以看到我放入的 cmets 中的所有文件,但它仍然会在承诺完成之前解决 Promise.all。我必须在解决承诺的方式上做错了。
  • 你想递归循环目录中的所有文件,然后将它们添加到数组中,然后上传它们吗?但是您需要等待所有这些都立即添加,因为每个item.file() 都是异步的?我会在item.file() 中返回一个新的new Promise(),然后用let promise = item.file(() =&gt; return new Promise(() =&gt; //add file here and call resolve)) 之类的变量捕获它,然后添加ptable.push(promise)。最后我会使用Promise.all()。我脑子里没有一个清晰的结构,但如果你能创建一个类似沙盒的例子,我可以试试。

标签: javascript angular promise es6-promise


【解决方案1】:

感觉你让事情变得有点太难了,我建议对编码标准和 Promises 的工作方式进行另一次阅读:) 引入了 async/await 构造以提高代码可读性。

无论如何,我这里有一些未经测试的代码。但它应该可以解决问题。另外,我强烈建议您添加类型。您使用的是 angular,所以我只能假设您使用的是 TypeScript。通过打字,您将减少错误,并且编译器会一路帮助您。

在我给出代码之前,这个webkitGetAsEntry 是非标准的。并且仅在您确实不想针对旧浏览器或 safari/ios 时使用:

非标准 此功能是非标准的,不在标准轨道上。不要在面向 Web 的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且行为可能会在未来发生变化。

但是,你可以这样做。处理事件的第一个函数。第二个遍历树:

async dndDropFiles(event: DragEvent): Promise<void> {
  if (event.dataTransfer.types[0] !== "Files" || !event.dataTransfer.items) {
    return;
  }

  const entries = [...(event.dataTransfer.items as any)].map(
    item => item.webkitGetAsEntry()
  );

  const allEntries = await this.dndTraverseFileTree(entries);

  const files = await Promise.all(
    allEntries.map(
      (entry) => new Promise((resolve, reject) => entry.file(resolve, reject))
    )
  );

  this.files = new Set(files);

  if (this.files.size > 0) {
    this.progress = this.uploadService.upload(
      this.files, this.currentDir, this.currentProject
    );
  }
}

async dndTraverseFileTree(entries: any[]): Promise<any[]> {
  const dirs = entries.filter(entry => !!entry && entry.isDirectory);
  const files = entries.filter(entry => !!entry && entry.isFile);

  if (dirs.length) {
    const childEntries = (
      await Promise.all(
          dirs.map(dir => new Promise(
            (resolve, reject) => dir.createReader().readEntries(resolve, reject))
          )
        )
    ).flat();

    return this.dndTraverseFileTree(childEntries);
  }

  return [ ...files ];
}

【讨论】:

  • 你为什么用 TypeScript 写作?这里还有一些东西还是可以简化的,你在allEntries.map(...)中也有语法错误
  • @PatrickRoberts 是的,非常欢迎您提出自己的答案,或者建议对我的答案进行编辑。您的评论有点没用:) 另外,allEntries.map 是语法错误吗?
  • 超过 10k,我不能建议编辑,我只能进行编辑,并且看到编辑不是微不足道的,也不够简短,无法发表评论。既然你邀请我就去做吧。
  • @PatrickRoberts 很抱歉你的工作,但我决定恢复它。 OP 正在使用角度,因此必须使用TypeScript。您所做的某些更新会导致代码失败,而某些更新会导致打字稿出现严格错误。我不喜欢这样:) 我喜欢严格输入所有内容(在我的回答中它已经不是,但你知道......)。我确实使用了您较短的解决方案,即使我认为它会降低可读性。还有你的flat(),虽然它需要在 tsconfig 中更改你的目标
  • 错了,错误回调没有任何东西可以调用,所以你构造的promise永远不会拒绝。
猜你喜欢
  • 2017-07-27
  • 1970-01-01
  • 2015-08-15
  • 1970-01-01
  • 2020-06-11
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
  • 1970-01-01
相关资源
最近更新 更多