承诺不等待请求完成

时间:2017-06-22 21:09:49

标签: javascript ajax promise

我试图将承诺链接在一起以提出一系列请求。这是我使用过承诺的少数几次之一,所以我不太清楚我在做什么。希望有人可以发现什么是错误的。基本上,我的第一个承诺是正常工作,然后第二个函数在第一个承诺解决后运行,但是,第三个函数不等待,因此我请求的信息不完整。

我可以看到我的函数正在返回正确的信息。所以主要是我的问题是如何格式化我的承诺,以便他们在运行之前等待彼此完成?

function getPaths(folderpath) {
  return new Promise(function(resolve, reject) {
    db.getPaths(folderpath, function(response) {
      //get paths for files and folders
      for (let i = 0; i < response.entries.length; i++) {
        let entries = response.entries[i];
        if (entries[".tag"] == "folder") {
          folderPaths.push(response.entries[i].path_lower);
        }
        if (entries[".tag"] == "file") {
          filePaths.push(response.entries[i].path_lower);
        }
      }
      resolve();
    });
  });
}

function openFolders(folders) {
  return new Promise(function(resolve, reject) {
    for (let i = 0; i < folders.length; i++) {
      db.getPaths(folders[i], function(response) {
        for (let j = 0; j < response.entries.length; j++) {
          let entries = response.entries[j];
          if (entries[".tag"] == "file") {
            filePaths.push(response.entries[j].path_lower);
          }
        }
        //console.log(filePaths); //returns correct information
      });
    }
    resolve();
  });
}

getPaths("/path").then(function() {
  openFolders(folderPaths);
}).then(function() {
  console.log(filePaths); //returns incomplete information
});

2 个答案:

答案 0 :(得分:3)

您在粗体

中遇到了一个大问题

function openFolders(folders) {
  return new Promise(function(resolve, reject) {
    for (let i = 0; i < folders.length; i++) {
      db.getPaths(folders[i], function(response) {
        // ...
      });
    }
    resolve();
  });
}

您正在围绕异步函数调用运行同步 for循环。循环完成同步迭代,然后立即调用resolve() - 异步处理程序中的代码(例如,filePaths.push(...))在 Promise被解析之前不运行

但你的问题并没有止步于此。您通过修改全局状态然后解析并清空Promise - resolve() vs resolve(someValue)来非常规地使用Promises。 @ FrankModica的答案还有更多的话要说。

至于补救措施,我建议你看看Promise.all - 一般情况下,以更传统的方式使用Promise

在下面的代码段中,我嘲笑了您的db函数,以便从虚假的内存数据库中读取fakedb。然后,我创建了一个getPaths函数,它包装了db.getPaths,但却返回了一个Promise。然后我重写openFolders来创建一个Promises数组,将它们传递给Promise.all,然后最终解析我想要的值

值得注意的是:Promise.all将在 parallel 中运行您的Promise数组 - 如果这不受欢迎,您可以使用.reduce.then连续运行它们。

&#13;
&#13;
const db = {
  getPaths: (path, k) => {
    setTimeout(k, 50, fakedb[path])
  }
}

function getPaths (path) {
  return new Promise((resolve, reject) =>
    db.getPaths(path, response => resolve(response)))
}

function openFolders (folders) {
  return Promise.all(folders.map(getPaths))
    .then(responses =>
      responses.reduce((acc, {entries}) =>
        acc.concat(entries.filter(x => x['.tag'] === 'file').map(x => x.path_lower)), []))
}

const fakedb = {
  'cat': {
    'entries': [
        { '.tag': 'dir', 'path_lower': './cat/.' },
        { '.tag': 'dir', 'path_lower': './cat/..' },
        { '.tag': 'file', 'path_lower': './cat/a' },
        { '.tag': 'file', 'path_lower': './cat/b' },
        { '.tag': 'file', 'path_lower': './cat/c' }
      ]
  },
  'dog': {
    'entries': [
        { '.tag': 'dir', 'path_lower': './dog/.' },
        { '.tag': 'dir', 'path_lower': './dog/..' },
        { '.tag': 'file', 'path_lower': './dog/a' },
        { '.tag': 'file', 'path_lower': './dog/b' },
        { '.tag': 'file', 'path_lower': './dog/c' }
      ]
  }
}

openFolders(['cat','dog']).then(console.log, console.error)
&#13;
&#13;
&#13;

我在openFolders中执行的操作可能会感觉有点复杂,因为它会处理responses处理程序中.then的所有转换 - 您可以( (可选))将工作分成多个.then调用,这可能会导致较轻的认知负荷

function openFolders (folders) {
  return Promise.all(folders.map(getPaths))
    // get `.entries` of each response
    .then(responses =>
      responses.map(x => x.entries))
    // flatten array of entries arrays into a single array
    .then(arrOfEntries =>
      arrOfEntries.reduce((acc, entries) =>
        acc.concat(entries), []))
    // only keep entries where `.tag` of each entry is `'file'`
    .then(entries =>
      entries.filter(x => x['.tag'] === 'file'))
    // return `path_lower` of the resulting entries
    .then(entries =>
      entries.map(x => x.path_lower))
}

答案 1 :(得分:2)

如果我理解正确,您正在使用外部范围(folderPathsfilePaths)中的一些变量。我不确定这是解决这个问题的最佳方法,但我相信一旦你从openFolders返回承诺,它就会开始工作。这样你就等到openFolders完成了:

getPaths("/path").then(function() {
  return openFolders(folderPaths);
}).then(function() {
  console.log(filePaths);
});

但我建议您解决下次通话所需的信息:

resolve(folderPaths);

resolve(filePaths);

所以你的代码看起来更像是:

getPaths("/path").then(function(folderPaths) {
  return openFolders(folderPaths);
}).then(function(filePaths) {
  console.log(filePaths); 
});

修改:看起来您可能还有@naomik提到的其他问题。如果db.getPaths是异步的,则在openFolders函数中,您可能会在循环中的所有异步调用完成之前解析。不幸的是,我现在没有时间来展示解决方案。希望@naomik可以!