hotwired / turbo

The speed of a single-page web application without having to write any JavaScript
https://turbo.hotwired.dev
MIT License
6.54k stars 415 forks source link

Turbo does not cancel pending requests that were prefetched on consecutive link clicks #1244

Open ilyadh opened 2 months ago

ilyadh commented 2 months ago

In a situation when page loads or internet connection are slow, clicking multiple links within a short period of time should only load the last link clicked. When prefetch is disabled, Turbo works correctly by canceling pending requests issued by previous clicks.

When prefetch is enabled however, all requests issued by clicks will run to completion. The page will re-render on each completed request resulting in flickering. And the last link clicked is not guaranteed to be the one visible when all requests complete.

In the following example, there are 3 links that take 2s,6s, and 4s to load. Clicking them consecutively in that order will result in the slowest page being opened last, even though it was a second click, not the last one.

Screen recording of example https://github.com/hotwired/turbo/assets/12209038/285444cb-d201-4d60-bf50-f64bb6f28e8d
Example with Rails ```ruby require "bundler/inline" gemfile(true) do source "https://rubygems.org" gem "rails" gem "puma" gem "propshaft" gem "turbo-rails" end require "action_controller/railtie" class App < Rails::Application config.load_defaults "7.1" config.consider_all_requests_local = true config.turbo.draw_routes = false routes.append do root to: "application#index" end end $template = DATA.read class ApplicationController < ActionController::Base include Rails.application.routes.url_helpers def index sleep(params[:sleep].to_i) if params[:sleep].present? render inline: $template, formats: :html end end App.initialize! Rack::Server.new(app: App, Port: 3000).start __END__

page <%= (params[:page].present? ? params[:page] : 'root').upcase %>

<%= link_to "root", root_path %>

without prefetch

  1. <%= link_to "slow", root_path(page: 'slow', sleep: 2) %>
  2. <%= link_to "slowest", root_path(page: 'slowest', sleep: 6) %>
  3. <%= link_to "slower", root_path(page: 'slower', sleep: 4) %>

with prefetch

  1. <%= link_to "slow", root_path(page: 'slow', sleep: 2) %>
  2. <%= link_to "slowest", root_path(page: 'slowest', sleep: 6) %>
  3. <%= link_to "slower", root_path(page: 'slower', sleep: 4) %>
```