Reputation: 952
I'm developing an JS simple project that requires taking the json result (via fetch() or XHR) from 2 services. The first is the main service, and the second acts as a fallback. Each of them can be slower than the other (hint: it's a dictionary app, and 2 service is Wiktionary and Google Translate).
To get a little faster speed, I'm thinking it would be better if I get both of the results asynchronously (in parallel). It still prefer the result of service #1 whether it's fast or slow, and ignore (or abort) the service #2's task.
But if it failed to get the result of service #1, then the result of service #2 will be used as alternative. And because both services are run in parallel (from one point of time), service #2's result can be returned as fast as possible.
Please see my pseudo example here.
const setTimeOutP = (time, label, re = false) => {
return new Promise((resolve, reject) => {
setTimeout(function(){
if(re == false)
resolve(label);
else
reject(label);
},time);
});
};
promiseOneMustBeReturnedUnlessReject([setTimeOutP(1000, "promise 1"), setTimeOutP(3000, "promise 2")]); // Promise 1 (in 1s), similar to Promise.race
promiseOneMustBeReturnedUnlessReject([setTimeOutP(3000, "promise 1"), setTimeOutP(1000, "promise 2")]); // Promise 1 (in 3s)
promiseOneMustBeReturnedUnlessReject([setTimeOutP(1000, "promise 1", true), setTimeOutP(3000, "promise 2")]); // Promise 2 (in 3s), NOT 4s
promiseOneMustBeReturnedUnlessReject([setTimeOutP(4000, "promise 1", true), setTimeOutP(2000, "promise 2")]); // Promise 2 (in 4s), NOT 6s
promiseOneMustBeReturnedUnlessReject([setTimeOutP(4000, "promise 1", true), setTimeOutP(2000, "promise 2", true)]); // Reject in 4s
I have a dirty solution right now that I think it would work like I described:
const printResult = async (key) => {
let dataObj = {
wiktionary: {
status: "pending"
},
googleTranslate: {
output: "",
status: "pending"
}
};
wiktionary(key).then(result => {
document.getElementById("result").innerHTML = result;
dataObj.wiktionary.status = "printed";
}).catch((error) => {
if (dataObj.googleTranslate.status == "ready") {
document.getElementById("result").innerHTML = dataObj.googleTranslate.output;
} else if (dataObj.googleTranslate.status == "error") {
throw new Error(error);
} else {
dataObj.wiktionary.status = "error";
}
});
googleTranslate(key).then(result => {
if (dataObj.wiktionary.status == "error") {
document.getElementById("result").innerHTML = result;
dataObj.googleTranslate.status = "printed";
} else {
dataObj.googleTranslate.output = result;
dataObj.googleTranslate.status = "ready";
}
}).catch((error) => {
if (dataObj.wiktionary.status == "error") {
throw new Error(error);
} else {
dataObj.googleTranslate.status = "error";
}
});
};
But is there any more elegant way to handle this situation?
Upvotes: 2
Views: 235
Reputation: 371049
You don't have to put .then
handlers on promises immediately - you can wait until they're necessary. If the promise has already been resolved by the time you put the new .then
on it, the new .then
will run immediately.
const printResult = (key) => {
const googleProm = googleTranslate(key);
const resultElm = document.getElementById("result");
wiktionary(key)
.then(mainResult => resultElm.textContent = mainResult)
.catch((mainError) => {
googleProm.then(googleResult => resultElm.textContent = googleResult)
.catch(googleError => throw new Error(error));
});
};
Upvotes: 1
Reputation: 225125
You can start both promises, then return the fallback in case of error:
const translate = key => {
const fallback = googleTranslate(key);
return wiktionary(key)
.catch(() => fallback);
};
used as follows:
const printResult = async key => {
const result = await translate(key);
document.getElementById("result").innerHTML = result;
};
If you have a cancellation mechanism, use it in a .then()
before the .catch()
.
Upvotes: 2