I’m trying to synchronize calendars, but the receiving server only wants "bite-sized" data, so I decided to sync PER USER PER MONTH.
Thus, I made a double loop:
First I loop through all users, and within that loop, I loop through the months if the date-range spans multiple months.
However, because the sync function is asynchronous, it executes multiple resources and months at the same time, because the loop doesn’t wait for completion.
I know similar questions have been asked before, but for some reason I just cannot get it to work.
Here are my functions:
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);
},
);
}
So to explain:
loopThroughMonths first checks if startDate and endDate differ more than 0 months.
If so, it looks through each month. If not, it just immediately executes loopThroughResources.
In case the dateRangeMonths spans multiple months, we loop through them using a for loop and perform the loopThroughResources function for every month.
Thus, if we say:
Synchronise resources A, B, C from 2023-12-27 till 2024-02-16
It will do:
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
The code works, but it is not waiting for all resources to complete (i.e. before the loopThroughResources function is done) before moving on to the next month.
For the resources, I even made it so that it waits until syncing resource A is complete before it proceeds to resource B, by calling the function from the $.post complete function, but I basically need another wait for the ENTIRE loopThroughResources function (and I’m guessing it needs to be something with Promises.all…)
I know I have to do something with promises, but I just can’t get it to work….
Any help would be greatly appreciated.
2
Answers
You’ll have a better time if you separate the concerns of what you want to do and doing it:
If you can promisify
$.post
into something you canawait
, you can get rid of the callbacks altogether and the execution becomes justI agree with AKX that the concerns of the program should be separated. One thing I’m doing differently is keeping everything as date objects until the very last moment when we use
console.log
to print the formatted date. This is useful because it allows other parts of our program to access the date properties. There is no advantage to generating all of the jobs beforehand. Other parts of the program are immensely simplified –