如何为含链式调用的Promise函数实现实时进度日志输出?
Great question! Tracking progress in promise chains—especially when they involve iterative API calls—can be tricky since promises themselves only resolve once. Here's how you can modify your code to add real-time progress updates tailored to your scenario:
Core Idea
We need to inject progress notifications at key points in your promise chain:
- When the process starts
- After each sub-task (like individual API calls in an iterator) completes
- When the entire chain finishes
We'll use a simple progress tracker utility to manage percentage calculations and trigger status updates, then integrate it into your promise functions and chain.
Step 1: Build a Progress Tracker Utility
First, create a reusable tracker to handle progress math and status callbacks:
// A simple utility to manage progress tracking function ProgressTracker(totalSteps, onStatusUpdate) { let currentStep = 0; // Notify when the process starts this.start = () => onStatusUpdate('starting'); // Call this after each sub-task completes to update progress this.markStepComplete = (message = 'in progress...') => { currentStep++; const completionPercent = Math.round((currentStep / totalSteps) * 100); onStatusUpdate(`${completionPercent}% ${message}`); }; // Notify when the entire process finishes this.complete = () => onStatusUpdate('100% completed'); }
Step 2: Modify Your Promise Functions to Report Progress
Update your promise1 and promise2 to accept the progress tracker, so they can report progress during iterative tasks:
// Example promise1 with an iterative API call loop function promise1(progressTracker) { return new Promise(async (resolve) => { // Simulate 3 API calls (adjust this to your actual iterator count) const apiEndpoints = ['/api/user-data', '/api/settings', '/api/history']; for (const endpoint of apiEndpoints) { // Simulate API request (replace with your actual fetch/axios call) await fetch(endpoint); // Update progress after each API call finishes progressTracker.markStepComplete(`finished ${endpoint}`); } resolve('promise1 completed'); }); } // Example promise2 with iterative tasks function promise2(progressTracker) { return new Promise(async (resolve) => { const apiEndpoints = ['/api/summary', '/api/export']; for (const endpoint of apiEndpoints) { await fetch(endpoint); progressTracker.markStepComplete(`finished ${endpoint}`); } resolve('promise2 completed'); }); }
Step 3: Integrate the Tracker into Your Promise Chain
Modify your promiseChain function to initialize the tracker and pass it through the chain. Since you mentioned res.json, we'll assume you're using Node.js/Express—note that you can't call res.json() multiple times (it closes the response), so we'll use res.write() to send chunked updates instead:
var promiseChain = function(res) { // Calculate total steps: 3 from promise1 + 2 from promise2 = 5 const totalSteps = 5; // Initialize tracker: pass a callback to send status updates via HTTP const progressTracker = new ProgressTracker(totalSteps, (status) => { // Send status as chunked JSON (client can listen for these chunks) res.write(JSON.stringify({ status }) + '\n'); }); // Trigger the starting status progressTracker.start(); // Run the promise chain with progress tracking promise1(progressTracker) .then(() => promise2(progressTracker)) .then(() => { progressTracker.complete(); res.end(); // Close the HTTP response once done }) .catch((error) => { // Send error status if something goes wrong res.write(JSON.stringify({ status: `Error: ${error.message}` }) + '\n'); res.end(); }); };
Simplified Version (No Iterative Tasks)
If your promise chain doesn't have internal iterators and just has two top-level promises, you can simplify the tracking:
var promiseChain = function(res) { const totalTasks = 2; let completedTasks = 0; // Send starting status res.write(JSON.stringify({ status: 'starting' }) + '\n'); promise1() .then(() => { completedTasks++; const percent = (completedTasks / totalTasks) * 100; res.write(JSON.stringify({ status: `${percent}% completed promise1` }) + '\n'); return promise2(); }) .then(() => { res.write(JSON.stringify({ status: '100% completed all tasks' }) + '\n'); res.end(); }); };
Key Notes
- HTTP Response Limitation: As mentioned,
res.json()can only be called once per request. Usingres.write()sends chunked data, which the client can parse incrementally. For frontend clients, you can usefetchwith a readable stream to listen for these updates. - Third-Party Promises: If you can't modify the original promise functions, wrap them in a helper to inject progress tracking:
function trackPromise(promiseFn, tracker, completionMessage) { return promiseFn().then(result => { tracker.markStepComplete(completionMessage); return result; }); } - Frontend Usage: If you're working in the browser instead of Node.js, replace the
res.write()callback with code that updates your UI (e.g.,document.getElementById('progress').textContent = status).
内容的提问来源于stack exchange,提问作者Ridhwaan Shakeel

