我正在尝试同步日历,但接收服务器只需要“一口大小”的数据,因此我决定每月同步每个用户。
因此,我做了一个双循环:
首先,我循环遍历所有用户,在该循环中,如果日期范围跨越多个月,我会循环遍历月份。
但是,由于同步函数是异步的,因此它会同时执行多个资源和月份,因为循环不会等待完成。
我知道以前也有人问过类似的问题,但由于某种原因我无法让它发挥作用。
这是我的功能:
function loopThroughMonths(resourceIds, startDate, endDate) {
startDateObject = new Date(startDate);
endDateObject = new Date(endDate);
// Check how many months our date range spans:
var dateRangeMonths = monthDiff(startDateObject, endDateObject);
if (dateRangeMonths > 0) {
// Loop through each month
for (let i = 0; i <= dateRangeMonths; i++) {
if (i == 0) {
// For the first month, the starting date is equal to the start of the date range
var loopStartDate = startDate;
var loopEndDate = formatDate(
lastDayOfMonth(
startDateObject.getFullYear(),
startDateObject.getMonth(),
),
);
}
if (i != 0 && i != dateRangeMonths) {
var loopMonth = new Date(
startDateObject.getFullYear(),
startDateObject.getMonth() + i,
1,
);
var loopStartDate = formatDate(
firstDayOfMonth(loopMonth.getFullYear(), loopMonth.getMonth()),
);
var loopEndDate = formatDate(
lastDayOfMonth(loopMonth.getFullYear(), loopMonth.getMonth()),
);
}
if (i == dateRangeMonths) {
// For the last month, the end date is equal to the last date of the date range.
var loopStartDate = formatDate(
firstDayOfMonth(
endDateObject.getFullYear(),
endDateObject.getMonth(),
),
);
var loopEndDate = endDate;
}
loopThroughResources(resourceIds, 0, loopStartDate, loopEndDate);
}
} else {
// Date range falls within 1 month, just proceed to looping through resources
loopThroughResources(resourceIds, 0, startDate, endDate);
}
}
function loopThroughResources(resourceIds, i, loopStartDate, loopEndDate) {
if (i == resourceIds.length) {
$("#start_exchange")
.text("Synchroniseren")
.removeAttr("disabled")
.css("cursor", "pointer");
return;
}
var resourceId = resourceIds[i];
$("#exchange_output").append(
"Start synchroniseren naar Outlook-agenda van " +
resourceNames.get(resourceId) +
" van " +
loopStartDate +
" tot " +
loopEndDate +
"...<br>",
);
$.post(
"sync.php",
{
resourceId: resourceId,
startDate: loopStartDate,
endDate: loopEndDate,
},
function (response) {
$("#exchange_output").append(response);
i = i + 1;
loopThroughResources(resourceIds, i, loopStartDate, loopEndDate);
},
);
}
所以解释一下:
loopThroughMonths 首先检查 startDate 和 endDate 的差异是否超过 0 个月。 如果是这样,它会查看每个月。如果没有,它会立即执行loopThroughResources。
如果 dateRangeMonths 跨越多个月,我们使用 for 循环遍历它们,并为每个月执行 LoopThroughResources 函数。
因此,如果我们说:
从2023-12-27到2024-02-16同步资源A、B、C
它会做:
Sync 2023-12-27 till 2023-12-31 (part of december 2023) for resource A
Sync 2023-12-27 till 2023-12-31 (part of december 2023) for resource B
Sync 2023-12-27 till 2023-12-31 (part of december 2023) for resource C
Sync 2024-01-01 till 2024-01-31 (whole january 2023) for resource A
Sync 2024-01-01 till 2024-01-31 (whole january 2023) for resource B
Sync 2024-01-01 till 2024-01-31 (whole january 2023) for resource C
Sync 2024-02-01 till 2024-02-16 (part of february 2023) for resource A
Sync 2024-02-01 till 2024-02-16 (part of february 2023) for resource B
Sync 2024-02-01 till 2024-02-16 (part of february 2023) for resource C
代码可以工作,但在进入下个月之前,它不会等待所有资源完成(即在loopThroughResources函数完成之前)。
对于资源,我什至通过从 $.post 完成函数调用该函数,使其等到同步资源 A 完成后再继续处理资源 B,但我基本上需要再次等待 ENTIRE LoopThroughResources 函数(我猜它需要是 Promises.all 的东西......)
我知道我必须做出一些承诺,但我就是无法让它发挥作用...... 任何帮助将不胜感激。
如果你将你想做的事情和正在做的事情分开,你会过得更好:
function toDate(start) {
return start.toISOString().split("T")[0];
}
function getFirstAndLastDayOfMonth(date) {
let start = new Date(date);
let end = new Date(date);
end.setMonth(end.getMonth() + 1);
end.setDate(0);
return {
start: toDate(start),
end: toDate(end),
};
}
function getDateRanges(startDate, endDate) {
let startDateObject = new Date(startDate);
let endDateObject = new Date(endDate);
let ranges = [];
let date = new Date(startDateObject);
date.setDate(15);
while (date < endDateObject) {
ranges.push(getFirstAndLastDayOfMonth(date));
date.setMonth(date.getMonth() + 1);
}
// Adjust start and end
ranges[0].start = toDate(startDateObject);
ranges[ranges.length - 1].end = toDate(endDateObject);
return ranges;
}
function getSyncJobs(resourceIds, startDate, endDate) {
const jobs = [];
const ranges = getDateRanges(startDate, endDate);
for (let resourceId of resourceIds) {
for (let range of ranges) {
jobs.push({
resourceId,
startDate: range.start,
endDate: range.end,
});
}
}
return jobs;
}
function doJob(params, done) {
// (do $.post here instead of `setTimeout`...)
console.log(params);
setTimeout(done, 500);
}
function go(done) {
// Build a queue of sync jobs we consume in `doNextJob`
const syncJobs = getSyncJobs(
["A", "B", "C"],
"2023-12-27",
"2024-02-16",
);
function doNextJob() {
console.log(`Remaining jobs: ${syncJobs.length}`);
if (syncJobs.length === 0) {
return done();
}
const job = syncJobs.shift();
doJob(job, doNextJob);
}
doNextJob();
}
go(function () {
console.log("All done!");
});
如果你可以将
$.post
承诺为你可以 await
的东西,你就可以完全摆脱回调,并且执行变得只是
async function doJob(job) {
console.log(job);
await new Promise(resolve => setTimeout(resolve, 500));
}
async function go() {
// Build a queue of sync jobs
const syncJobs = getSyncJobs(
["A", "B", "C"],
"2023-12-27",
"2024-02-16",
);
for(let job of syncJobs) {
await doJob(job);
}
console.log("All done!");
}