【问题标题】:How to use JS async/await to wait for AJAX response如何使用 JS async/await 等待 AJAX 响应
【发布时间】:2019-08-14 16:38:02
【问题描述】:

我第一次在 Javascript 中使用 async/await 函数。我无法让我的脚本等待 AJAX 响应,然后再继续读取/使用该响应。

我知道这里已经有很多关于 async/await 函数没有按预期等待的问题,但其他问题的答案似乎对我没有用。

基本上,我要做的是遍历图层名称数组(对于OpenLayers 地图)和forEach 图层名称我正在发送 AJAX 调用以检索记录(如果它存在) 来自 MySQL 数据库。然后我简单地显示结果,转到下一层名称,发送下一个 AJAX 调用,等等。

这是我的代码:

async function getCellLayers() {
    layerNames = [];
    map.getLayers().forEach(function(layer) {
        if (layer.get('type') == "cell") {
            if (layer.getZIndex() == 100) {
                layerNames.push(layer.get('name'));
                if (layerNames.length == 1) {
                    fullExtent = layer.getSource().getExtent();
                } else {
                    ol.extent.extend(fullExtent, layer.getSource().getExtent());
                }
            }
        }
    });
    return layerNames;
}

async function getRecord(cell_date) {
    $.ajax({
        url: 'rec/getRecord/'+cell_date,
        type: 'get',
        dataType: 'json',
        success: await function(response){
          console.log("getRecord response: "+JSON.stringify(response));
          return response['data'];
      }
    });
}

async function testAsyncAwaitFunction() {
    let layerNames = await getCellLayers();
    layerNames.forEach(async function(layerName) {
        cell_date = layerName.substring(3)+"_"+window['currentImage'].substring(17,25);
        console.log(cell_date+":");
        let cellRecord = await getRecord(cell_date);
        console.log("Matches: "+cellRecord.length);
        console.log("testAsyncAwaitFunction response: "+JSON.stringify(cellRecord));
    });
}

我希望在控制台中看到类似的内容:

cell101_20190202:
getRecord response: {"data": [{"id":1,"record":"cell101_20190202","value":"0.8"}]}
Matches: 1
testAsyncAwaitFunction response: {"data": [{"id":1,"record":"cell101_20190202","value":"0.8"}]}
cell102_20190202:
getRecord response: {"data": [{"id":2,"record":"cell102_20190202","value":"0.7"}]}
Matches: 1
testAsyncAwaitFunction response: {"data": [{"id":2,"record":"cell102_20190202","value":"0.7"}]}
[ ... and so on ... ]

但我却得到了这个:

cell101_20190202:
cell102_20190202:
(...)
getRecord response: {"data": [{"id":1,"record":"cell101_20190202","value":"0.8"}]}
getRecord response: {"data": [{"id":2,"record":"cell102_20190202","value":"0.7"}]}
(...)
getRecord response: {"data": [{"id":14,"record":"cell202_20190202","value":"0.6"}]}
(200x) Uncaught (in promise) TypeError: Cannot read property 'length' of undefined
getRecord response: {"data": [{"id":15,"record":"cell204_20190202","value":"0.5"}]}
(...)

我从未见过以testAsyncAwaitFunction response 为前缀的JSON.stringify 行,可能是因为尝试获取cellRecord 长度的console.log 命令之前的行由于AJAX 响应尚未到达而失败。

我怀疑以下行将是这里的关键:

let cellRecord = await getRecord(cell_date);

但我不知道为什么那个似乎没有“等待”,即使上面几行的另一行似乎工作得很好:

let layerNames = await getCellLayers();

非常感谢能更好地掌握使用 async/await 的人的帮助。我更习惯于 PHP 和 Python,并且很难将我的思维方式转变为异步思考。

【问题讨论】:

  • 为什么要等待函数声明?
  • 在使用await 调用它之前将所有函数更改为Promise。
  • 感谢大家的所有回复 - 非常感谢您的帮助。

标签: javascript ajax async-await


【解决方案1】:

getRecord改成这个

function getRecord(cell_date) {
    return $.ajax({
        url: 'rec/getRecord/'+cell_date,
        type: 'get',
        dataType: 'json'
    }).then(function(response){
      console.log("getRecord response: "+JSON.stringify(response));
      return response['data'];
  });
}

并从代码中的所有位置删除 asyncawait 关键字,但这两部分中的 testAsyncAwaitFunction 除外:

async function testAsyncAwaitFunction()

let cellRecord = await getRecord(cell_date);

否则你不需要它们。

之前它不会起作用,因为您的函数需要返回一个包含数据的承诺。你应该阅读JavaScript promises。 Async/Await 在很大程度上是这些的语法糖,用于处理异步代码。您拥有的唯一实际异步代码是对getRecord 的调用。

【讨论】:

  • 我读了很多关于 Promise 的内容,但我无法正确理解它们的工作原理。你链接的那个资源看起来比我读过的其他资源有用得多。感谢您的帮助。
【解决方案2】:

关于 async 需要记住的一点是,任何带有 async 前缀的函数都应该返回一个 Promise。 getRecord 应该返回你所拥有的。此外,虽然您的外部函数 testAsyncAwaitFunction 是异步的,并且您的 forEach 回调是异步的,但您无需等待 forEach 的所有承诺来解决。

你想要这个模式:

async function testAsyncAwaitFunction() {
    let layerNames = await getCellLayers();
    const promises = [];
    layerNames.forEach(function(layerName) {
        promises.push(getRecord(cell_date));
    });
    const cell_records = await Promise.all(promises);
    cell_records.forEach(function(cell_record, idx) {
        cell_date = layerNames[idx].substring(3)+"_"+window['currentImage'].substring(17,25);
        console.log(cell_date+":");
        console.log("Matches: "+cellRecord.length);
        console.log("testAsyncAwaitFunction response: "+JSON.stringify(cellRecord));
    })
}

【讨论】:

【解决方案3】:

这里有两点: - 你的getRecord 函数没有返回Promise因此,await 不会等待任何东西 - forEach不能使用异步函数,因为实现不等待。

对于第一个问题,您可以通过以下方式解决:

async function getRecord(cell_date) {
    return $.ajax({
        url: 'rec/getRecord/'+cell_date,
        type: 'get',
        dataType: 'json',
    })
    .then(response => response.data);
}

对于第二个问题,你可以这样运行循环:

async function testAsyncAwaitFunction() {
    let layerNames = await getCellLayers();
    for (layerName of layerNames) {

        cell_date = layerName.substring(3)+"_"+window['currentImage'].substring(17,25);
        console.log(cell_date+":");
        let cellRecord = await getRecord(cell_date);
        console.log("Matches: "+cellRecord.length);
        console.log("testAsyncAwaitFunction response: "+JSON.stringify(cellRecord));

    }
}

但是这样做会使一切都运行起来。您可以通过发送请求然后使用Promise.all 等待所有请求完成来做得更好:

const promises = []
for (layerName of layerNames) {
        cell_date = layerName.substring(3)+"_"+window['currentImage'].substring(17,25);
        console.log(cell_date+":");
        promises.push(getRecord(cell_date));
}
const records = await Promise.all(promises)

【讨论】:

  • 感谢弗朗索瓦 - 这是我最终使用的解决方案。最后一部分的加分,当然大大提高了速度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-17
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 2021-12-11
  • 2016-03-08
  • 1970-01-01
相关资源
最近更新 更多