如何跟踪while循环进度,实现后台加载并完成后显示结果?
Got it, let's break down how to add background processing with real-time progress tracking to your database loop. Here's a practical, step-by-step solution that keeps users informed without leaving them staring at a frozen screen:
Right now, your synchronous loop blocks the browser until all 88 entries are processed—this is why users see no progress for 35-40 seconds. We need to split the work into two parts:
- A backend that handles processing in the background
- A frontend that polls for progress updates and displays them
We'll use PHP sessions to track progress (you could also use a cache like Redis for scaling, but sessions work for most small-scale use cases).
Script 1: Initialize the Process (start_process.php)
This script sets up the progress tracking and starts the background processing:
<?php session_start(); // Connect to database to get total entry count $conn = mysqli_connect('localhost', 'your_user', 'your_pass', 'your_db'); $totalQuery = mysqli_query($conn, "SELECT COUNT(*) AS total FROM your_table"); $totalRow = mysqli_fetch_assoc($totalQuery); $totalItems = $totalRow['total']; mysqli_close($conn); // Initialize progress in session $_SESSION['processing'] = [ 'total' => $totalItems, 'completed' => 0, 'results' => [], 'status' => 'running' ]; // Start background processing (using exec to avoid blocking the browser) // Note: Adjust the path to process_batch.php as needed exec("php /path/to/process_batch.php > /dev/null 2>&1 &"); // Return total count to frontend header('Content-Type: application/json'); echo json_encode(['total' => $totalItems]); ?>
Script 2: Process Entries in Batches (process_batch.php)
This script runs in the background, processes entries one by one, and updates the session progress:
<?php session_start(); // Exit if no process is initialized if (!isset($_SESSION['processing'])) exit; $conn = mysqli_connect('localhost', 'your_user', 'your_pass', 'your_db'); // Track offset to avoid reprocessing entries if (!isset($_SESSION['processing']['offset'])) { $_SESSION['processing']['offset'] = 0; } // Process 1 entry per run (adjust to batch size if you want faster progress updates) $limit = 1; $query = mysqli_query($conn, "SELECT * FROM your_table LIMIT {$_SESSION['processing']['offset']}, $limit"); while ($row = mysqli_fetch_assoc($query)) { // Call external site (use cURL for better error handling) $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://external-site.com/api?param=" . urlencode($row['your_column'])); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $externalData = curl_exec($ch); curl_close($ch); // Store result and update progress $_SESSION['processing']['results'][] = [ 'entry_id' => $row['id'], 'external_data' => $externalData ]; $_SESSION['processing']['completed']++; $_SESSION['processing']['offset']++; // Release session lock to let frontend read progress session_write_close(); session_start(); } mysqli_close($conn); // Mark process as done if all entries are processed if ($_SESSION['processing']['completed'] >= $_SESSION['processing']['total']) { $_SESSION['processing']['status'] = 'done'; } else { // Re-run the script to process next entry (loop until done) exec("php /path/to/process_batch.php > /dev/null 2>&1 &"); } ?>
Script 3: Get Current Progress (get_progress.php)
This script returns the latest progress data to the frontend:
<?php session_start(); header('Content-Type: application/json'); if (!isset($_SESSION['processing'])) { echo json_encode(['error' => 'No process started']); exit; } echo json_encode($_SESSION['processing']); ?>
Create a simple interface that starts the process, polls for updates, and shows results when done:
HTML
<div class="progress-wrapper"> <div class="progress-bar" id="progressBar"></div> <p id="progressText">0% Complete</p> </div> <div id="results" style="display: none;"> <h2>Processing Complete!</h2> <ul id="resultsList"></ul> </div> <button onclick="startProcessing()" id="startBtn">Start Processing</button>
CSS
.progress-wrapper { width: 100%; height: 24px; border: 1px solid #ddd; border-radius: 12px; overflow: hidden; margin: 20px 0; } .progress-bar { height: 100%; width: 0%; background-color: #2ecc71; transition: width 0.3s ease; }
JavaScript
function startProcessing() { document.getElementById('startBtn').disabled = true; // Initialize process fetch('start_process.php') .then(res => res.json()) .then(data => { if (data.total) { // Start polling for progress pollProgress(); } }); } function pollProgress() { fetch('get_progress.php') .then(res => res.json()) .then(data => { if (data.status === 'done') { // Update to 100% and show results updateProgress(100); displayResults(data.results); // Cleanup session fetch('cleanup_session.php'); // Optional: create a script to unset $_SESSION['processing'] } else { const percent = Math.round((data.completed / data.total) * 100); updateProgress(percent); // Poll again after 500ms (matches your per-entry processing time) setTimeout(pollProgress, 500); } }); } function updateProgress(percent) { const bar = document.getElementById('progressBar'); const text = document.getElementById('progressText'); bar.style.width = `${percent}%`; text.textContent = `${percent}% Complete (${data.completed}/${data.total})`; } function displayResults(results) { const resultsContainer = document.getElementById('results'); const resultsList = document.getElementById('resultsList'); resultsContainer.style.display = 'block'; results.forEach(result => { const li = document.createElement('li'); li.textContent = `Entry ${result.entry_id}: ${result.external_data}`; resultsList.appendChild(li); }); }
- Session Locking: Use
session_write_close()after updating progress to prevent the frontend from waiting for the session lock to release. - Error Handling: Add try/catch blocks around external API calls to handle failures without breaking the entire process.
- Batch Size: If you want fewer backend calls, increase the
$limitinprocess_batch.php(e.g., process 5 entries at once) and adjust the poll interval accordingly. - Cleanup: Add a
cleanup_session.phpscript to clear the session data after processing finishes, so users can run the process again.
内容的提问来源于stack exchange,提问作者Born2DoubleUp

