【问题标题】:Really run fetch multiple times synchronously真正同步运行 fetch 多次
【发布时间】:2018-05-16 23:30:15
【问题描述】:

我见过各种使 fetch 看起来是同步的解决方案,例如使用 await,但是它们必须包装在 async 函数中,所以整个事情就不是同步的。

我需要运行 fetch x 次,并按照开始的顺序完成每次迭代。如果我在 fetch 之后使用 .then,它会在每个 fetch 实例之后运行,但结果(包括 .then 运行的函数的结果)按响应顺序返回,因此是一个列表按 a-z 顺序获取的 url,可能会在输出时返回部分随机顺序,这是不可接受的。

我尝试过将 jquery ajax 与 async: false 一起使用,虽然这会使输出与输入保持一致——当它成功运行时——它会在错误时无限期冻结,并且它的超时值在同步时不起作用模式。

我们似乎可以选择以潜在的随机顺序获取多个连续 url,或者我们可以按顺序获取它们,在同步模式下使用 jquery ajax / XMLHTTPRequest,但是当一个 URL 时冻结浏览器超时,而我们设置为中止尝试检索 url 的超时值被忽略。

这是一个示例,它读取地址文本文件,每行一个,获取他们的市议会区,并将其附加到该行。它可以工作——除了输出行的顺序与输入的顺序不同,而且我还没有找到一种方法让它们在同步模式下在 url 超时时无限期冻结。

<!DOCTYPE html>
<html><head><body>
  <h1>LAS - Find My District Widget</h1>
  <h2>Select a text file (.txt) with a list of addresses - each in a new line.  The Widget will return the corresponding districts.</h2>
  <input id="input-file" type="file">
  <hr><textarea id="output-box" cols="250"></textarea>
<script>
var baseUrl = "https://changedthistoprotectprivacy";
var input = document.getElementById('input-file');
var output = document.getElementById('output-box');

function writeSuccesOutput(geoclientResp) {
  var address = geoclientResp.input.replace(/(?:\r\n|\r|\n)/g,"");
  var cityCouncil = geoclientResp['results'][0].response.cityCouncilDistrict;
  var addressWithCcd = address + ' City Council District ' + cityCouncil + '\n';
  output.textContent += addressWithCcd;
}

function writeErrorOutput(error) {
  var address = geoclientResp.input.replace(/(?:\r\n|\r|\n)/g,"");
  var message = address + ' ERROR ' + error + '\n';
  output.textContent += message;
}

function geocode(address) {
  var querystring = encodeURI(address);
  var url = baseUrl + querystring;
  fetch(url).then(function(response) {
    response.json().then(writeSuccesOutput);
  }).catch(writeErrorOutput);
}

var reader = new FileReader();
reader.onloadend = function (event) {
  output.textContent = ''
  var addressList = event.target.result.split("\n");
  for (var i = 0; i < addressList.length; i++) {
    geocode(addressList[i]);
  }
}

input.onchange = function() {
  if (this.files && this.files[0]) {
    reader.readAsBinaryString(this.files[0]);
  }
}
</script>
</body>
</html>

【问题讨论】:

  • 如果你没有异步,你的结果顺序将是先到者。
  • 与 xmlhttprequest 不同,fetch 不能“进行”同步 - 停止尝试,拥抱异步 - 您需要串行请求还是并行请求?

标签: javascript jquery ajax fetch


【解决方案1】:

您的实际要求是保持结果与输入的顺序相同,以便我们可以确定哪些坐标属于哪个地址。您可以使用Promise.all(),它会在给定所有承诺解析并以相同顺序返回结果时解析。

Promise.all()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

var reader = new FileReader();
reader.onloadend = function (event) {
  output.textContent = ''
  var addressList = event.target.result.split("\n");
  var prmises = addressList.map(address => geocode(address));
  Promise.all(promises).then(coordinates => {
    // here coordinates[0] is result for addressList[0] and so on
    
  });
}

// Here is a demo
const fakeApi = (address) => {
   const randrom = Math.random() * 1000 + 1000;
     return new Promise(resolve => {
     setTimeout(() => resolve(`${address}_${randrom}`), 10);
   });
};

const addresses = ['A', 'B', 'C', 'D', 'E', 'F'];

const promises = addresses.map(address => fakeApi(address));
console.log(addresses);
Promise.all(promises).then(results => {
  console.log(results);
});

【讨论】:

  • 好的,但是这会等待所有请求完成,然后将它们完全添加到它们应该去的地方。 O(n^2) 时间。 OP 询问是否有办法按顺序接收并在它们出现时插入,因此当同时运行时,他可以在O(n) 时间内完成。或者O(2n),如果你想这样说
  • @ytpillai 阅读 Promise.all() 文档。它不会占用O(n^2)。时间取决于最慢的 api 调用,所有请求都将异步执行,Promise.all() 将等待所有响应。
  • 你说得对,对不起,我脑子有问题。两者都一样
  • @ytpillai 没问题。 A 很害怕,你要投反对票。我发生了很多次:)
  • 我通常会尝试先评论和讨论,然后再讨论。不幸的是,大多数 SO 用户只是直接投反对票,甚至没有任何解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-04
  • 2017-01-03
  • 1970-01-01
相关资源
最近更新 更多