I found this previous thread (How to perform same action regardless of promise fulfilment?), but it's 5 years old and references winjs is a kludge.
What I would like to do is load a list of data elements. I've got local copies of the list and local copies of the elements -- but they may have changed on the server side.
That process should work like this: load the LIST from the database into the local storage (Comparing against the local) --> THEN load the (multiple) DATA ELEMENTS from the database that are listed in the LIST.
So if the "loadList" async function succeeds... I want to run the "loadElements" async function. If the loadList function rejects... I STILL want to run the "loadElements" function (Which fires off multiple fetch requests - one for each element).
"Use 'finally'" I hear you say... but I want to pass the results of the "loadList" resolve/reject and "loadElements" resolve/reject functions to the calling function. 'finally' doesn't receive or pass properties as far as I know.
The reason I want to pass the results to the calling function is to see if the rejection reasons are acceptable reasons and I can trust the local copy as the authoritative copy or not (for example, if the DB doesn't contain the LIST, I can trust that the local list is the authoritative version)... so I need a way to analyze the 'failures' within the calling function.
Here is what I have:
export function syncLinkTablesAndElementsWithDB(username) {
return (dispatch, getState) => {
return new Promise((resolve, reject) => {
dispatch(loadLinkTableAndElementsFromDB(STATIONS_LINK_TABLE_TYPE, username))
.then((msg) => {
console.log("loadLinkTableAndElementsFromDB RESOLVED: ", msg);
resolve(msg)
})
.then(() => {
dispatch(pushLinkTableToDB(STATIONS_LINK_TABLE_TYPE, username))
dispatch(pushAllUserStationsToDB(username))
})
.catch((allPromReasons) => {
console.log("loadLinkTableAndElementsFromDB REJECTED: ", allPromReasons);
allReasonsAcceptable = true;
allPromReasons.forEach(reason => {
if (!isAcceptableLoadFailureReasonToOverwrite(reason)) {
allReasonsAcceptable = false;
}
});
if (allReasonsAcceptable) {
//TODO: DO push of local to DB
// eventually return results of the push to DB...
} else {
reject(allPromReasons)
}
})
});
}
}
export function loadLinkTableAndElementsFromDB(tableType, username) {
return (dispatch, getState) => {
return new Promise((resolve, reject) => {
dispatch(loadLinkTableFromDB(tableType, username))
.then(successMsg => {
resolve(Promise.all([successMsg, dispatch(loadAllUsersStationsFromDB(username)).catch(err=>err)]))
})
.catch(err => {
reject(Promise.all([err, dispatch(loadAllUsersStationsFromDB(username)).catch(err=>err)]))
})
});
}
}
export function loadAllUsersStationsFromDB(username) {
return (dispatch, getState) => {
return new Promise((resolve, reject) => {
let linkTable = getStationsLinkTable(username); // get the local link table
if (linkTable && Array.isArray(linkTable.stations)) { // if there is a local station list
let loadPromises = linkTable.stations.map(stationID => dispatch(loadStationFromDB(stationID)).catch((err) => err));
Promise.all(loadPromises)
.then((allReasons) => {
let allSuccesses = true;
allReasons.forEach(reason => {
if (!reason.startsWith(SUCCESS_RESPONSE)) {
allSuccesses = false;
}
});
if (allSuccesses) {
resolve(SUCCESS_RESPONSE + ": " + username);
} else {
reject(allReasons);
}
})
} else {
return reject(NO_LINK_TABLE_AVAILABLE + ": " + username);
}
});
};
}
loadStationFromDB and loadLinkTableFromDB do what you'd expect... try to load those things from from the DB. I can include their code if you think it's worthwhile.
----------- EDIT ----------- To clarify what I'm trying to accomplish:
I'm trying to sync local storage with a database. I want to do this by pulling the data from the database, compare the time/datestamps. This will make the local storage version the authoritative copy of all the data. After the loads from the DB, I'd like to then push the local storage version up to the DB.
I need to care for the fact that the database will often simply not have the data at all, and thus might 'reject' on a pull... even though, in the instance of a sync, that rejection is acceptable and should not stop the sync process.
Per suggestions below, I've modified my code:
export function loadLinkTableAndElementsFromDB(tableType, username) {
console.log("loadLinkTableAndElementsFromDB(", tableType, username, ")");
return (dispatch, getState) => {
return new Promise((resolve, reject) => {
dispatch(loadLinkTableFromDB(tableType, username))
.then(successMsg => {
console.log("loadLinkTableFromDB RESOLVED: ", successMsg)
resolve(Promise.all([successMsg, dispatch(loadAllUsersStationsFromDB(username)).catch(err => err)]))
})
.catch(err => {
console.log("loadLinkTableFromDB REJECTED: ", err)
reject(Promise.all([err, dispatch(loadAllUsersStationsFromDB(username)).catch(err => err)]))
})
});
}
}
export function syncLinkTablesAndElementsWithDB(username) {
console.log("syncLinkTablesAndElementsWithDB(", username, ")");
return (dispatch, getState) => {
dispatch(loadLinkTableFromDB(STATIONS_LINK_TABLE_TYPE, username))
.then((successLoadLinkTableMsg) => {
console.log('Successfully loaded link table: ', successLoadLinkTableMsg)
return dispatch(pushLinkTableToDB(STATIONS_LINK_TABLE_TYPE, username))
})
.catch((rejectLoadLinkTableReason) => {
console.log("Failed to load link table from DB: " + rejectLoadLinkTableReason);
if (allReasonsAcceptableForOverwrite(rejectLoadLinkTableReason)) { // some rejection reasons are accectable... so if failed reason is okay....
console.log("Failure to load link table reasons were acceptable... pushing local link table anyway");
return dispatch(pushLinkTableToDB(STATIONS_LINK_TABLE_TYPE, username))
} else {
console.log("Throwing: ", rejectLoadLinkTableReason);
throw rejectLoadLinkTableReason;
}
})
.then((successPushLinkTaleMsg) => {
console.log("Successfully pushed link table: " + successPushLinkTaleMsg);
return dispatch(loadAllUsersStationsFromDB(username)); // I want this to occur regardless of if the link table stuff succeeds or fails... but it must occur AFTER the loadLinkTableFromDB at least tries...
})
.catch((rejectPushLinkTableReason) => {
console.log("Failed to push link table: " + rejectPushLinkTableReason);
return dispatch(loadAllUsersStationsFromDB(username)); // I want this to occur regardless of if the link table stuff succeeds or fails... but it must occur AFTER the loadLinkTableFromDB at least tries...
})
.then((successLoadAllUserStationsMsg) => {
console.log("Successfully loaded all user stations: " + successLoadAllUserStationsMsg);
return dispatch(pushAllUserStationsToDB(username))
})
.catch((rejectLoadAllUserStationsReason) => {
console.log("Failed to push all users stations: " + rejectLoadAllUserStationsReason);
if (allReasonsAcceptableForOverwrite(rejectLoadAllUserStationsReason)) { // some rejection reasons are accectable... so if failed reason is okay....
console.log("Load users stations reasons are acceptable...");
return dispatch(pushAllUserStationsToDB(username))
} else {
console.log("throwing: ", rejectLoadAllUserStationsReason);
throw rejectLoadAllUserStationsReason;
}
})
.then((successPushAllUserStationsMgs) => {
console.log("Successfully pushed all users stations: " + successPushAllUserStationsMgs);
return Promise.resolve();
})
.catch((rejectPushAllUserStationsReason) => {
console.log("Failed to push all users stations: " + rejectPushAllUserStationsReason);
throw rejectPushAllUserStationsReason;
})
};
}
export function syncAllWithDB(username) {
return (dispatch, getState) => {
// other stuff will occur here...
dispatch(syncLinkTablesAndElementsWithDB(username)) // *** Error here ***
.then((successMsg) => {
console.log("Successful sync for : " + successMsg);
})
.catch(allReasons => {
console.warn("Link tables and elements sync error: ", allReasons);
})
// });
}
}
Unfortunately, I'm now getting getting 'TypeError: dispatch(...) is undefined' on the dispatch in the syncAllWithDB function. This function hasn't changed...