htmx icon indicating copy to clipboard operation
htmx copied to clipboard

With multiple htmx.ajax requests only the first call is executed

Open RogierdeRuijter opened this issue 2 years ago • 14 comments

When calling htmx.ajax multiple times only the first call is actually executed. For example when executing the code below. Only my-page-1 is requested.

htmx.ajax("GET", `/my-page-1.html`);
htmx.ajax("GET", `/my-page-2.html`);

It seems like the my-page-2 call is cancelled.

Reproduction link is here

RogierdeRuijter avatar Jan 02 '24 12:01 RogierdeRuijter

Im having a similar issue, except that only the second request is executed.

Path-17 avatar Jan 05 '24 07:01 Path-17

My solution was to use fetch to retrieve data and then insert the result into the DOM using JavaScript. I stopped using htmx for this problem.

RogierdeRuijter avatar Jan 05 '24 20:01 RogierdeRuijter

Hey, you might want to take a look at hx-sync

drop - drop (ignore) this request if an existing request is in flight (the default)

By default, if a second request is fired on an element that already has one in flight, that new request will be dropped thus cancelled

What may not be intuitive in your case here, is that calling htmx.ajax without the third parameter will default using the body as the source element of your requests. So with the default hx-sync strategy, any request happening while one is already in flight will be ignored

You could:

  • Define hx-sync="queue all" for example on your body element
  • Use different elements as the source of your htmx.ajax calls so they don't even collide at all

Telroshan avatar Jan 07 '24 19:01 Telroshan

Having a similar issue but for me the second request is not running.

  htmx.ajax('GET', "/cart/products", {target:"#cart_products", swap:"innerHTML"});
  htmx.ajax('GET', "/cart/summary", {target:"#cart_summary", swap:"innerHTML"});
  htmx.ajax('GET', "/cart/products_count", {target:"#cart_product_count", swap:"innerHTML"});

mfdebby avatar Mar 15 '24 13:03 mfdebby

this:queue all does not seem to be working on the body tag. I'll try to investigate this further @Telroshan

amrojjeh avatar Jun 19 '24 09:06 amrojjeh

I'm having trouble understanding when is the queuedRequest supposed to be cleared in the issueAjaxRequest function. It seems to only occur when a new request is sent, yet while debugging I've found that the htmx internal data sometimes resets, causing the queue to reset (no idea why or what's causing it)? And even then, shouldn't the queue sending requests as the element becomes free rather than waiting for a new request? Or maybe I'm just misunderstanding the source code. For further clarity on the problem, my goal is to make the below work:

<!DOCTYPE html>
<html>
  <head>
    <script src="htmx.js"></script>
  </head>
  <body hx-sync="this:queue all">
    <p id="questionid123">first</p>
    <p id="questionid124">second</p>
    <p id="questionid125">third</p>
    <p id="questionid0">fourth</p>
    <script>
      setTimeout(() => htmx.ajax('GET', '/question/partial?fieldId=0', { source: "body", target: '#questionid0'}), 2000);

      var array= ['123','124','125'];
      for(i=0;i<array.length;i++) { 
          htmx.ajax('GET', '/question/partial?fieldId='+array[i], { source: "body",
          target: '#questionid'+array[i] });
      }
    </script>
  </body>
</html>

amrojjeh avatar Jun 19 '24 19:06 amrojjeh

This issue arises when ajax is called while other requests sent via ajax are still in fight, even when the targets are different elements. The root cause has to do with the XHR being stored on the internal data for the target elements and the fact that, for some reason, ajax is using the internal data for the body element even when a target parameter is provided. Thus, it seems to get confused and think there's already a request in flight for the body element when if it were doing the right thing it wouldn't necessarily think there are any requests in flight for the body.

I fixed the issue for myself with the following change:

   /**
   * Issues an htmx-style AJAX request
   *
   * @see https://htmx.org/api/#ajax
   *
   * @param {HttpVerb} verb
   * @param {string} path the URL path to make the AJAX
   * @param {Element|string|HtmxAjaxHelperContext} context the element to target (defaults to the **body**) | a selector for the target | a context object that contains any of the following
   * @return {Promise<void>} Promise that resolves immediately if no request is sent, or when the request is complete
   */
  function ajaxHelper(verb, path, context) {
    verb = (/** @type HttpVerb */(verb.toLowerCase()))
    if (context) {
      if (context instanceof Element || typeof context === 'string') {
-       return issueAjaxRequest(verb, path, null, null, {
+       return issueAjaxRequest(verb, path, resolveTarget(context), null, {
          targetOverride: resolveTarget(context),
          returnPromise: true
        })
      } else {
        return issueAjaxRequest(verb, path, resolveTarget(context.source), context.event,
          {
            handler: context.handler,
            headers: context.headers,
            values: context.values,
            targetOverride: resolveTarget(context.target),
            swapOverride: context.swap,
            select: context.select,
            returnPromise: true
          })
      }
    } else {
      return issueAjaxRequest(verb, path, null, null, {
        returnPromise: true
      })
    }
  }

I don't understand why it was passing null for the elt parameter to issueAjaxRequest, so maybe this will have some unintended consequences, but it fixed this issue for me.

HTH and happy to collab more on this.

patreeceeo avatar Jul 19 '24 00:07 patreeceeo

I'll roll with this change for now and if it doesn't cause me any trouble I'll issue a PR (if I don't forget ;))

patreeceeo avatar Jul 19 '24 00:07 patreeceeo

fwiw, the above solution is still working fine for me, but I see that the maintainers don't want PRs without explicitly asking for them, so there's nothing for me to do for now. Their automated tests are also cool with this change.

patreeceeo avatar Jul 29 '24 01:07 patreeceeo

Sorry I meant to test this as well but I've not gotten to it yet. I'll be sure to test this on my end as well. But thank you for your work!

amrojjeh avatar Jul 29 '24 02:07 amrojjeh

I see that the maintainers don't want PRs without explicitly asking for them

Only for new features @patreeceeo ! Cf the contribution guidelines

  1. Please do not PR new features unless you have already made an issue proposing the feature, and had it accepted by a core maintainer.
  2. Correspondingly, it is fine to directly PR bugfixes for behavior that htmx already guarantees

You're ofc welcome to submit a bugfix PR. We'll then investigate on whether this is the best fix or not, make sure it doesn't break anything etc., but you can open that FR if you feel like it

Telroshan avatar Jul 29 '24 07:07 Telroshan

Oh, I totally misread... I'll open that bugfix PR.

patreeceeo avatar Jul 29 '24 19:07 patreeceeo