【问题标题】:How do I limit an ajax call to the scope with the provided parameter?如何使用提供的参数限制对范围的 ajax 调用?
【发布时间】:2020-08-09 03:23:28
【问题描述】:

我正在尝试根据存储的(数组)值“填充”表单中的选择:city_selector_vars。这个存储的值显示了正确的值(见下面的代码)。

我遍历这个数组并尝试为定义的 stateCode 获取城市 (get_states)。我将 stateCode 作为值传递给函数。

如果我在$.post 之前登录state_data.country_code,我会得到预期的回报。如果我在$.post 中记录state_data.country_code,它并不总是与传递的国家代码相同。我认为这与范围有关,但我的知识不足以弄清楚。

/**
 * city_selector_vars
 * [
 *      'countryCode' => 'NL',
 *      'stateCode' => 'NL-FL',
 *      'cityName' => 'A name'
 * ]
 * [
 *      'countryCode' => 'BE',
 *      'stateCode' => 'BE-BR',
 *      'cityName' => 'Another name'
 * ]
 */
if ( true === Array.isArray(city_selector_vars) ) {
    for (i = 0; i < city_selector_vars.length; i++ ) {
        get_states(city_selector_vars[i].countryCode, (response)=> {
            // console.log(response);
        });
    }
}

function get_states(countryCode, callback) {
    const state_data = {
        action: 'get_states_call',
        country_code: countryCode
    };
    // console.log(state_data.country_code) shows correct country code
    $.post(ajaxurl, state_data, (response)=> {
        // here the wrong response first shows
        // state_data.country_code is not always the same as state_data.country_code
        callback(response);
    });
}
function get_states_call( $country_code = false ) {
    // $country_code = always false, even though it is passed
    if ( false == $country_code ) {
        if ( isset( $_POST[ 'country_code' ] ) ) {
            // here it sometimes picks up the incorrect value
            $country_code = $_POST[ 'country_code' ];
        }
    }
    // code removed, since depends on code above
    $items = [];
    echo json_encode( $items );
    die();
}

因为它只是有时会拾取错误的值,我认为它与范围有关,但我没有足够的知识来确定它。

【问题讨论】:

  • 那是因为 AJAX 是异步的。你不知道回调的执行顺序。
  • 是的,但这并不能回答问题:) 你只是在陈述一个事实。
  • 是的,我已经尝试过他的解决方案,但无法让它发挥作用,可能是因为我没有提供更多关于“内部”get_states 的信息。
  • 我之前已经在 get_states 中添加了它作为参数,但据我所知,这似乎没有起到作用。
  • @mateleco 我刚刚更新了改编后的代码,并附有 muka.gergely 的一些指针。使用 async/await 我每次只得到 1 个结果。当我删除它(但保留他的其余代码)时,我得到了预期的结果。更新代码中的 console.log (可以找到 here 给了我预期的结果,但要使用它,我需要解析承诺值,这就是它现在再次弄乱订单的地方。

标签: javascript php jquery scope


【解决方案1】:

从您的代码看来,您正在实现一个查询 Wordpress 数据的函数。

我不认为您的问题是可变范围的问题 - 而是 异步函数 的问题。您向每个国家/地区发送一个 AJAX 请求 - 每个请求“单独”传送到服务器,由服务器处理,然后返回响应。 无法确保响应的到达顺序与请求发出的顺序相同。

考虑一下:

const arr = []

function getSingleItem(id) {
  fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
    .then(response => response.json())
    .then(json => {
      console.log(json.id)
    })
}

function getAllItems() {
  for (let i = 1; i < 10; i++) {
    getSingleItem(i)
  }
}

getAllItems()

您可以在控制台中看到,响应没有按照请求发送的顺序到达(json.id 实际上是从 1 到 i 的运行数字 - 它们应该是按顺序到达的。

您必须自己负责订购:

const arr = []

function getSingleItem(id) {
  return fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
    .then(response => response.json())
    .then(json => {
      return json.id
    })
}

async function getAllItems() {
  for (let i = 1; i < 10; i++) {
    const r = await getSingleItem(i)
    arr.push(r)
  }
  console.log(arr)
}

getAllItems()

通过在函数中设置async await,我可以确定,响应会按照请求发出的顺序显示。 (await 不会“让”for() 循环走得更远,直到 awaited 响应没有到达 - 有点像同步代码。)

在这种情况下,我使用 fetch() 函数返回一个 Promise 对象 - 一个可以在我的其他函数中等待的对象。

您的代码

您的 Javascript 代码可以修改:

/**
 * city_selector_vars
 * [
 *      'countryCode' => 'NL',
 *      'stateCode' => 'NL-FL',
 *      'cityName' => 'A name'
 * ]
 * [
 *      'countryCode' => 'BE',
 *      'stateCode' => 'BE-BR',
 *      'cityName' => 'Another name'
 * ]
 */
async function get_all_state(city_selector_vars) {
  if (true === Array.isArray(city_selector_vars)) {
    // preparing the response array
    const response_states = []
    for (i = 0; i < city_selector_vars.length; i++) {
      // try - catch to handle errors
      try {
        // await the response
        const d = await get_states(city_selector_vars[i].countryCode);
        // add response to the response array
        response_states.push(d)
      } catch (err) {
        // handle error
        console.log(err)
      }
    }
    // return the array - in order!
    return response_states
  }
}

function get_states(countryCode, callback) {
  const state_data = {
    action: 'get_states_call',
    country_code: countryCode
  };
  // console.log(state_data.country_code) shows correct country code

  // returning a Promise object, so await works in the other function
  return new Promise((resolve, reject) => {
    $.post(ajaxurl, state_data, (response) => {
      // here the wrong response first shows
      // state_data.country_code is not always the same as state_data.country_code
      // resolving the Promise when the response arrives
      resolve(response)
    });
  })


}

在您修改后的代码中,必须显式创建 Promise(记住 - fetch() 返回 Promise?),因此它可以由 async-await 处理。

我认为 sn-p 应该可以正常工作,但如果不是,则只需要进行小调试 :)

wordpress 注释

如果您使用 Wordpress 发送 JSON,建议使用 wp_send_json() 函数。

更多:https://developer.wordpress.org/reference/functions/wp_send_json/

【讨论】:

  • 感谢您的广泛回复。在查看您的建议(并对其进行测试)时,我觉得一些信息(从我这边丢失)。下面解释。 > 从您的代码看来,您正在实现一个查询 Wordpress 数据的函数。你是对的,你可以找到插件仓库here。更具体地说,JS 文件是here。 1/2
  • 我删除了该示例的一些代码,但可能有点太多了。如果您单击第二个链接,它将带您进入“导致”问题的功能。您的代码部分工作,进行了一些编辑,但它仅适用于 1 个实例,我认为这是我认为缺少的信息,明智的......我需要调用 get_states 并将每个实例的结果添加到 city_selector_vars 中,所以在循环中(imo) ,因为它是ACF repeater field 的一部分。注册。 wp_send_json;会调查的。 2/2
  • 我只是再次测试您的解决方案,但我最终得到一个包含 2 个相同数组的数组,而不是 2 个不同的集合。我得到国家 NL 的 2 倍州,而不是 NL + BE。
  • 抱歉我错了...我得到 1x NL...但不是 BE。
  • 我做了一些调整,我似乎越来越接近了。请参阅更新的代码here。 console.log 每次都会给我我期望的结果。然而,promise.resolve 又把订单搞砸了 :)
【解决方案2】:

至于admin_post_edit_load_states函数,我认为Promise.all是正确的做法:

function admin_post_edit_load_states() {
    if (true === Array.isArray(city_selector_vars)) {
        // preparing the response array
        const response_states = []
        for (i = 0; i < city_selector_vars.length; i++) {
            // try - catch to handle errors
            try {
                // await the response
                const d = get_states(city_selector_vars[i].countryCode);
                // add response to the response array
                response_states.push(d)
            } catch (err) {
                // handle error
                console.log(err)
            }
        }
        console.log(response_states);

        Promise.all(response_states).then(function(jsonResults) {
            var instance_count = 0;
            for (i = 0; i < jsonResults.length; i++) {
                var obj = JSON.parse(jsonResults[i]);
                var len = obj.length;
                var $stateValues = '';
                var select_state = $('select[name*="row-' + instance_count + '"][name*="stateCode"]');
                var stored_state = city_selector_vars[instance_count].stateCode;
                select_state.fadeIn();
                for (j = 0; j < len; j++) {
                    $selected = '';
                    var state = obj[j];
                    var current_state = state.country_code + '-' + state.state_code;
                    if (current_state === stored_state) {
                        $selected = ' selected="selected"';
                    }
                    var selected = $selected;
                    $stateValues += '<option value="' + state.country_code + '-' + state.state_code + '"' + selected + '>' + state.state_name + '</option>';
                }
                select_state.append($stateValues);
                instance_count++;
            }
        });
    }
}

这应该在异步的同时保持原始顺序。 注意我们在这里做什么:

首先,我们用承诺填充数组response_states(每次调用 get_states 都会返回一个承诺)。然后,Promise.all(response_states) 创建一个新的 Promise,当 response_states 数组中的所有 Promise 都被解析时,它就会被解析。最后,参数 jsonResults 是一个数组,其中包含数组 response_states 中每个已解析的 Promise 的对应值。

让我们看看它是否有效。

【讨论】:

  • 嘿@mateleco,感谢您的回复。我按原样使用了您的代码,前几个 3 测试都用预期值填充了选择,因此看起来很有希望(没有双关语)。我也会将您的代码应用于“城市选择”。将进行更多测试,并让您知道结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-10
  • 1970-01-01
相关资源
最近更新 更多