使用Promise.all()获取带有等待语句的URL列表

时间:2018-04-24 16:24:02

标签: javascript

tl; dr - 如果你必须过滤承诺(比如错误的承诺),请不要使用异步功能

我试图获取一个带异步的网址列表并解析它们,问题是如果我在提取时其中一个网址出错 - 请让我们这样做。 s由于某种原因说api端点不存在 - 程序破解解析时出现明显的错误:

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): TypeError: ext is not iterable

我已经尝试检查res.json()是否未定义,但显然不是因为它抱怨整个' ext'承诺数量不可迭代。

async function fetchAll() {
  let data
  let ext
  try {
    data = await Promise.all(urls.map(url=>fetch(url)))
  } catch (err) {
    console.log(err)
  }
  try {
    ext = await Promise.all(data.map(res => {
      if (res.json()==! 'undefined') { return res.json()}
    }))
  } catch (err) {
    console.log(err)
  }
  for (let item of ext) {
    console.log(ext)
  }
}

问题1:

如何解决上述问题,以免因无效地址而崩溃?

问题2:

我的下一步是将提取的数据写入数据库。 假设数据大小为2-5mgb的内容,是我使用Promise.all()内存效率的方法吗?或者它是否会更节省内存,否则编写一个for循环来处理每个fetch然后在同一次迭代中写入数据库然后才处理下一次获取?

8 个答案:

答案 0 :(得分:4)

从根本上说,您的代码存在一些问题。我们应该按顺序解决这些问题,首先是你没有传递任何URL!

async function fetchAll(urls) {
  let data
  let ext
  try {
    data = await Promise.all(urls.map(url=>fetch(url)))
  } catch (err) {
    console.log(err)
  }
  try {
    ext = await Promise.all(data.map(res => {
      if (res.json()==! 'undefined') { return res.json()}
    }))
  } catch (err) {
    console.log(err)
  }
  for (let item of ext) {
    console.log(ext)
  }
}

首先,您在DEPENDENT DATA上有几个try catch块。它们应该都在一个try catch块中:

async function fetchAll(urls) {
  try {
    let data = await Promise.all(urls.map(url=>fetch(url)))
    let ext = await Promise.all(data.map(res => {
      // also fixed the ==! 'undefined'
      if (res.json() !== undefined) { return res.json()}
    }))
    for (let item of ext) {
      console.log(ext)
    }
  } catch (err) {
    console.log(err)
  }
}

接下来是res.json()返回一个包裹在对象周围的promise(如果它存在)的问题

if (res.json() !== undefined) { return res.json()}

这不是你应该如何使用.json()方法。如果没有可解析的json,它将失败。你应该把一个.catch放在上面

async function fetchAll(urls) {
  try {
    let data = await Promise.all(urls.map(url => fetch(url).catch(err => err)))
    let ext = await Promise.all(data.map(res => res.json ? res.json().catch(err => err) : res))
    for (let item of ext) {
      console.log(ext)
    }
  } catch (err) {
    console.log(err)
  }
}

现在,当它无法获取URL或解析JSON时,您将收到错误并且它会在没有抛出的情况下级联下来。现在你的try catch块只会在发生不同的错误时抛出。

当然这意味着我们在每个promise上放置一个错误处理程序并级联错误,但这并不是一件坏事,因为它允许所有的提取发生,并且你可以区分哪些提取失败。这比为所有提取物设置通用处理程序并且不知道哪个提取失败要好得多。

但现在我们以一种形式,我们可以看到可以对代码执行一些更好的优化

async function fetchAll(urls) {
  try {
    let ext = await Promise.all(
      urls.map(url => fetch(url)
        .then(r => r.json())
        .catch(error => ({ error, url }))
      )
    )
    for (let item of ext) {
      console.log(ext)
    }
  } catch (err) {
    console.log(err)
  }
}

现在,我们可以通过更小的占用空间,更好的错误处理以及可读,可维护的代码来决定我们最终想要返回的内容。现在,该函数可以存在于任何地方,可以重用,并且只需要一个简单的GET URL数组。

下一步是对它们做一些事情,所以我们可能想要返回数组,这将包含在一个promise中,实际上我们希望错误起泡,因为我们已经处理了每个fetch错误,所以我们也应该删除尝试捕获。在这一点上,使异步不再有帮助,并且主动伤害。最后,我们得到一个小功能,将所有网址解析或错误与各自的网址分开,我们可以轻松过滤,映射和链接!

function fetchAll(urls) {
  return Promise.all(
    urls.map(url => fetch(url)
      .then(r => r.json())
      .then(data => ({ data, url }))
      .catch(error => ({ error, url }))
    )
  )
}

现在我们回到一个类似对象的数组,每个对象都有它获取的url,以及数据或错误字段!这使得链接和检查SUPER变得容易。

答案 1 :(得分:0)

而不是第5行的fetch(url),而是创建自己的函数customFetch,它调用fetch但可能返回null或错误对象,而不是抛出。

类似

async customFetch(url) {
    try {
       let result = await fetch(url);
       if (result.json) return await result.json();
    }
    catch(e) {return e}
}

答案 2 :(得分:0)

关于问题1 ,请参阅:

Handling errors in Promise.all

  

Promise.all全有或全无。它会在阵列中的所有承诺解析后解析,或者在其中一个承诺拒绝后立即拒绝。换句话说,它可以使用所有已解析值的数组进行解析,也可以使用单个错误进行拒绝。

答案 3 :(得分:0)

if (res.json()==! 'undefined')

没有任何意义是一个异步函数。删除该条件,然后返回res.json()

try {
    ext = await Promise.all(data.map(res => res.json()))
} catch (err) {
    console.log(err)
}

您的方法是否最好"或者"记忆效率高"是辩论。提出另一个问题。

答案 4 :(得分:0)

您收到TypeError: ext is not iterable - 因为ext在您发现错误并且没有为其分配数组时仍然是undefined。试图遍历它会抛出一个你没有捕获的异常。

我猜你正在寻找

async function fetchAll() {
  try {
    const data = await Promise.all(urls.map(url => fetch(url)));
    const ext = await Promise.all(data.map(res => res.json()));
    for (let item of ext) {
      console.log(ext)
    }
  } catch (err) {
    console.log(err)
  }
}

答案 5 :(得分:0)

您可以通过捕获错误并返回一个稍后会过滤掉的特殊fetch对象来使jsonFail失败:

function Fail(reason){this.reason=reason;};
const isFail = o => (o&&o.constructor)===Fail;
const isNotFail = o => !isFail(o);
const fetchAll = () =>
  Promise.all(
    urls.map(
      url=>
        fetch(url)
       .then(response=>response.json())
       .catch(error=>new Fail([url,error]))
    )
  );

//how to use:
fetchAll()
.then(
  results=>{
    const successes = results.filter(isNotFail);
    const fails = results.filter(isFail);

    fails.forEach(
      e=>console.log(`failed url:${e.reason[0]}, error:`,e.reason[1])
    )
  }
)

关于问题2:

根据您获得的网址数量,您可能需要throttle您的请求,如果网址来自一个大文件(千兆字节),您可以使用stream结合限制。

答案 6 :(得分:0)

var myClosure: (Int) -> ()

答案 7 :(得分:0)

如果您不依赖于每个资源都取得成功,我会回到基础知识跳过异步/等待

我会处理每个获取个体,所以我可以捕获错误

的错误
function fetchAll() {
  const result = []
  const que = urls.map(url => 
    fetch(url)
    .then(res => res.json())
    .then(item => {
      result.push(item)
    })
    .catch(err => {
      // could't fetch resource or the
      // response was not a json response
    })
  )

  return Promise.all(que).then(() => result)
}

好东西@TKoL说:

  

Promise.all错误,只要其中一个内部promises错误,所以无论任何人给你的建议,它将归结为 - 确保你将promises包装在错误处理程序中,然后将它们传递给Promise.all