如何在crawler.js的.each()函数中调用外部超时函数实现每轮循环1秒延迟?
Got it, let's tackle this problem. The key issue here is that jQuery's .each() runs synchronously—so if you just slap a timeout inside it, all the delays will fire almost at the same time instead of waiting for each iteration to finish first. Here's how to fix this while leveraging your external_functions.js utility:
Step 1: Prepare the Delay Function in external_functions.js
First, make sure your delay function is set up to work with async logic (or callbacks, if you need legacy support). Let's define it properly:
Modern ES Module Version
// external_functions.js export function delay(ms) { // Return a Promise to enable async/await usage return new Promise(resolve => setTimeout(resolve, ms)); }
Legacy Browser Version (No ES Modules)
If you need to support older environments, attach the function to the global window object:
// external_functions.js window.delay = function(ms) { return new Promise(resolve => setTimeout(resolve, ms)); };
Step 2: Rewrite the Crawler Loop in crawler.js
We can't use jQuery's .each() directly because it doesn't respect asynchronous waits. Instead, we'll use a manual loop with async/await (modern) or recursion (legacy) to ensure each iteration waits 1 second before moving on.
Option 1: Modern Async/Await Approach (Recommended)
This is clean and readable for modern browsers:
// crawler.js // Import the delay function (skip this if using the global window version) import { delay } from './external_functions.js'; async function crawlProducts() { const productTiles = $(".product-tile"); // Loop through each tile manually (instead of .each()) for (let i = 0; i < productTiles.length; i++) { const $tile = $(productTiles[i]); // Your existing product processing logic const product = []; const product_id = parseInt($tile.attr("data-itemid").replace(/-/g, "")); const product_price = $tile.find(".price").text().trim(); // ... add more product data extraction here // Wait 1 second AFTER processing the current tile await delay(1000); } } // Start the crawler crawlProducts();
Option 2: Legacy Recursive Approach (For Older Browsers)
If you can't use async/await, use a recursive function to iterate one tile at a time:
// crawler.js // Use the global delay function (from window) const delay = window.delay; function processNextTile(index, productTiles) { // Exit condition: no more tiles to process if (index >= productTiles.length) return; const $tile = $(productTiles[index]); // Your existing product processing logic const product = []; const product_id = parseInt($tile.attr("data-itemid").replace(/-/g, "")); const product_price = $tile.find(".price").text().trim(); // ... add more product data extraction here // Wait 1 second, then process the next tile delay(1000).then(() => { processNextTile(index + 1, productTiles); }); } // Start the crawler const productTiles = $(".product-tile"); processNextTile(0, productTiles);
Key Notes
- Why not use
.each()? jQuery's.each()executes all iterations synchronously. Even if you add asetTimeoutinside, all timeouts will be scheduled at nearly the same time, so you won't get the 1-second gap between iterations. - If your delay function uses callbacks instead of Promises: If
external_functions.jshas a callback-based delay likefunction waitOneSecond(callback) { setTimeout(callback, 1000); }, you can wrap it in a Promise forasync/awaituse, or use the callback directly in the recursive approach.
内容的提问来源于stack exchange,提问作者Bindl

