How to stop Promise.race() from resolving early? - javascript

I'm racing three promises. One of them should never resolve unless a specific condition is met, but it still resolves itself.
myFunction() is meant to wait for a blockchain transaction to get confirmed. It can have statuses: confirmed, unconfirmed and failed. My issue is that statusCheck gets resolved and returns nothing when receiving "unconfirmed".
I wish to make this function strictly:
resolve on "confirmed"
reject on "failed"
ignore "unconfirmed" and wait until successListener resolves
How should I approach this?
public async myFunction() {
const successListener = rxjs.firstValueFrom(transactionConfirmationObservable)
.then(async result => {
return result;
// Listens for transaction status change to "confirmed"
});
const failureListener = rxjs.firstValueFrom(transactionFailureObservable)
.then(async result => {
throw new Error(result);
// Listens for transaction status change to "failed"
});
const statusCheck = axios(currentTransacionStatusRequest)
.then(async result => {
if (result == "confirmed") return result;
if (result == "failed") throw new Error(result);
// Checks status in case it became confirmed/failed before the listeners opened.
//
// Expected beviour: if result is "unconfirmed" - do nothing.
// Actual behaviour: resolves even with result "unconfirmed".
});
return Promise.race([successListener, failureListener, statusCheck]);

Fixed it by creating new promises so I could explicitly resolve and reject them. Now I can prevent statusCheck from resolving itself when it finishes execution.
public async myFunction() {
const successListener = new Promise(async (resolve, reject) => {
const result = await rxjs.firstValueFrom(transactionConfirmationObservable);
resolve(result);
});
const failureListener = new Promise(async (resolve, reject) => {
const result = await rxjs.firstValueFrom(transactionFailureObservable);
reject(new Error(result));
});
const statusCheck = new Promise(async (resolve, reject) => {
const result = await axios(currentTransacionStatusRequest);
if (result == "confirmed") resolve(result);
if (result == "failed") reject(new Error(result));
});
return Promise.race([successListener, failureListener, statusCheck]);
}

Related

Promise JavaScript Returning Empty Array

createFolder() function is returning an empty array. I am not sure what I am doing wrong but it needs to return the items within project_array
function get_project_folders(){
return new Promise((resolve, reject)=>{
fs.readdir(__dirname + '/projects', (error, data1)=>{
if(error){
reject(console.log(`Error. Unable to read directory - ${error}`))
}else{
resolve(data1)
}
})
})
}
async function createFolder(){
let list_of_projects = await get_project_folders()
let project_array = []
return new Promise((resolve, reject)=>{
for(let project of list_of_projects){
let splitProject = project.split("-")
fs.readdir(__dirname + `/projects/${splitProject[0]}-${splitProject[1]}`, (error, data1)=>{
if(error){
console.log('Error. Unable to read directory.')
}else{
project_array.push({circuit: splitProject[0], fuse: splitProject[1], pole: data1})
}
})
}
resolve(project_array)
})
}
async function testIt(){
let folderData = await createFolder()
console.log(folderData)
}
testIt()
This is a classic, what you are doing is resolving the promise with the empty array before your node fs async methods have resolved. Try this instead:
async function createFolder(){
const list_of_projects = await get_project_folders();
const result = await Promise.all( list_of_projects.map(project => new Promise((resolve, reject) => {
const splitProject = project.split("-");
fs.readdir(__dirname + `/projects/${splitProject[0]}-${splitProject[1]}`, (error, data1) => {
if(error){
console.error('Error. Unable to read directory.');
resolve( null );
} else {
resolve({
circuit: splitProject[0],
fuse: splitProject[1],
pole: data1
});
}
});
});
// Filter out the errors that resolved as `null`
return result.filter( Boolean );
}
In essence, wrap every fs. call in a promise, then use Promise.all to generate an array of promises. Because Promise.all requires all to be resolved for it to be resolved, make sure you even resolve when there is an error - just return something falsy (in my case null) so you can filter it out later.

How to avoid propagation when chaining Promise with Non-Promise calls

I want the following code not to call OK logic, nor reject the promise. Note, I've a mixture of promise and non-promise calls (which somehow still managed to stay thenable after returning a string from its non-promise step), I just want the promise to stay at pending status if p1 resolves to non-OK value.
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
}
else {
return Promise.resolve("OK");
}
}).then(result => {
console.error("OK logic...");
});
You've got two options:
1) throw an error:
p1.then(result => {
if (result =='notOk') {
throw new Error('not ok');
} else {
return 'OK';
}
})
.then(r => {
// r will be 'OK'
})
.catch(e => {
// e will be an error with message 'not ok', if it threw
})
the second .then won't run, the .catch will.
2) decide what to do in the latter .then conditionally:
p1.then(result => {
if (result =='notOk') {
return 'not ok'
} else {
return 'OK';
}
})
.then(r => {
if (r === 'OK') {
// do stuff here for condition of OK
}
})
This works because the second .then takes as an argument whatever was returned by the previous .then (however if the previous .then returned a Promise, the second .then's argument will be whatever was asyncronously resolved)
Note: if you .catch a promise that errored, and you return THAT promise, the final promise WON'T have an error, because the .catch caught it.
The best approach is to not chain that way. Instead do this:
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
} else {
return Promise.resolve("OK")
.then(result => {
console.error("OK logic...");
});
}
})
If you're writing a linear chain, that means you're saying that you want step by step execution, which isn't what you want in this case.
Alternatively, if your target platforms/build system support it, write it as an async function:
(async function() {
const result = await Promise.resolve "NOT_OK_BUT_NOT_A_CATCH_NEITHER");
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
} else {
await Promise.resolve("OK");
console.error("OK logic...");
}
})();
Found a way, not sure how good is it but it works. The idea is to have it as promises all the way and just not resolve when not needed.
In my case it saves me a hassle with managing ignorable results without polluting result with the likes of processable flags.
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
return new Promise((resolve, reject) => {
if (result === "OK") {
resolve(result);
}
// OR do nothing
console.error("Just Do nothing");
});
}).then(result => {
console.error("OK logic...");
});

Rejecting a Promise by passing a Promise

Why is it exactly that the a resolveing promise correctly waits for the someOtherPromise to complete, but the reject does not? Run the following code sample and check the console.log output. I expected the "myFailingPromise rejected" message to show 2000 ms later, just as the "myPromise resolved" did.
let someOtherPromise = (previousPromise) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(previousPromise + ' => someOtherPromise after 2000ms');
resolve('someOtherPromise');
}, 2000);
});
}
let myPromise = () => {
return new Promise((resolve, reject) => {
resolve(someOtherPromise('myPromise'));
});
};
let myFailingPromise = () => {
return new Promise((resolve, reject) => {
reject(someOtherPromise('myFailingPromise'));
});
};
myPromise().then((val) => {
// this is executed after `someOtherPromise` resolves.
console.log('myPromise resolved');
}).catch((err) => {
console.log('myPromise rejected');
});
myFailingPromise().then((val) => {
// this is executed after `someOtherPromise` resolves.
console.log('myFailingPromise resolved');
}).catch((err) => {
console.log('myFailingPromise rejected');
});
I know the intended behaviour can be achieved by using someOtherPromise.then(reject) in the second example, but my question is why the promise as an argument to reject is not possible, since it works for resolve.
When you're resolving a promise A, if the value with which you're resolving it is a promise B, then your promise A will match the state of the promise B.
However, when you're rejecting a promise, your only giving a reason, whether the reason looks or not like a promise doesn't matter.
What you need to do, is to resolve with someOtherPromise in both cases.
If you want to wait for the first promise and reject anyway, you can do this:
let myFailingPromise = () => {
return new Promise((resolve, reject) => {
someOtherPromise.then(reject);
});
};
The reject only takes in a reason to highlight the error. Not another promise. You can resolve a promise with another promise of the same type, yet only the last promise's success case you will see.
Have a look at this adapted implementation:
const someOtherPromise = new Promise((resolve, _) => {
resolve("I am a success");
});
const failingPromise = new Promise((_, reject) => {
reject("I failed for a reason");
});
someOtherPromise
.then((result) => {
console.log("some other promise resolves", result);
failingPromise
.then((success) => {
console.log("never called");
})
.catch((reason) => {
console.error("failing promise rejects", reason);
});
}
)
.catch((error) => {
console.error("also never called", error);
});
This is the then-able approach to wait for other promises leading to a callback hell. This is why you can also use async / await syntax:
const app = async () => {
try {
const success1 = await someOtherPromise; // will succeed
console.log(success1);
const success2 = await failingPromise; // never succceds
console.log(success2); // never be reached
} catch (e) {
return Promise.reject(e); // catches the error of failing promise and rethrows it, redundant but here showcases error handling
}
}
;
app()
.then(() => {
console.log("never be reached because of failing promise");
})
.catch(console.error);
In regards to your updated question with the timeout, here is what you could do in order to always await another promise:
const otherPromise = async (willBeSuccesful: boolean) => {
console.log("started timer for case", willBeSuccesful);
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("resolve timer for case", willBeSuccesful);
const successValue = "Fnord"; // whatever you want
return willBeSuccesful
? resolve(successValue)
: reject("this other promise failed because of reasons"); // only provide a reason, not another promise
});
};
};
const alwaysWaitForOtherPromiseThenRejectAnyway = async (otherPromise) => {
try {
const success = await otherPromise; // always waits 2 seconds, not matter
console.log("other promises succeeded with value", success);
} catch (e) {
return Promise.reject(e); // passing through reason, redundant, only to showcase
}
return Promise.reject("reason why this promise failed"); // only happens after otherPromise was resolved, you could swallow that error and fail here or resolve here as well
};
const succeedingPromise = otherPromise(true);
const failingPromise = otherPromise(false);
alwaysWaitForOtherPromiseThenRejectAnyway(succeedingPromise)
.catch((reason) => console.error(reason)); // fail after waiting for success of other promise
alwaysWaitForOtherPromiseThenRejectAnyway(failingPromise)
.catch((reason) => console.error(reason)); // will fail as soon as otherPromise fails
It will always wait for the timeout before rejection happens. Rejection will have different reasons. Output will be:
started timer for case true
started timer for case false
resolve timer for case true
resolve timer for case false
other promises succeeded with value Fnord
reason why this promise failed
this other promise failed because of reasons

How to resolve Web3 promises objects? [duplicate]

Im trying to use async await on a function that returns a promise but the out put im getting is Promise { <pending> }. In here im using function called convertFiletoPDF which returns a promise. I need to get the output (the path that i have mention in resolve() ).
When i use it as
convertFiletoPDF(file).then((result) => {
console.log(result);
}).catch((err)=>{
console.log(err);
});
it gives the expected result.Whats wrong with the code below? im quite new to these async await and promises.
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
unoconv.convert(file, "pdf", function(
err,
result
) {
if (err) {
reject(err);
}
let File = file.substring(file.lastIndexOf("/")+1,file.lastIndexOf("."));
// result is returned as a Buffer
fs.writeFile(__dirname+"/files/converted/"+File+".pdf", result, error => {
/* handle error */
if (err) reject(error);
else resolve("./files/converted/"+File+".pdf");
});
});
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc(file);
console.log(res);
The return value of an async function is a promise, so naturally that's what your console.log outputs. You need to either consume the result via await (within another async function) or use then/catch (within another async function).
This is what you're currently doing:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc("some file");
console.log(res);
You need to be doing either this:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
(async() => {
try {
let res = await myfunc("some file");
console.log(res);
} catch (e) {
// Deal with the fact there was an error
}
})();
or with then and catch:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
myfunc("some file")
.then(res => {
console.log(res);
})
.catch(e => {
// Deal with the fact there was an error
});
convertFiletoPDF()
This function run and returned a Promise. This is fine.
myfunc()
Lets say myfunc takes 10 seconds. Javascript starts to wait newly created thread result from libuv via event loop mechanism. So, Javascript says, "That one is async, I will not wait, when it finishes it will let me know and i will run my then callback and then I will proceed with its output."
Javascript keeps his promise. Tries to run next below lines. myFunch is still working. Output is not ready yet. Returns undefined.
let res = myfunc(file);
console.log(res);
You get undefined.
Someone might find this example from my code useful. You can wrap it in a promise and then resolve the custom promise and then call another promise to confirm the receipt of the original web3 call.
return new Promise((resolve, reject) => {
tokenContract.methods.approve(
exchangeAddress,
BIG_NUMBER_1e50
)
.send({ from })
.once('transactionHash')
.once('receipt', receipt => resolve(receipt))
.on('confirmation')
.on('error', err => reject(err))
.then( receipt => // will be fired once the receipt its mined
console.log(receipt),
);
});

Using async await on custom promise

Im trying to use async await on a function that returns a promise but the out put im getting is Promise { <pending> }. In here im using function called convertFiletoPDF which returns a promise. I need to get the output (the path that i have mention in resolve() ).
When i use it as
convertFiletoPDF(file).then((result) => {
console.log(result);
}).catch((err)=>{
console.log(err);
});
it gives the expected result.Whats wrong with the code below? im quite new to these async await and promises.
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
unoconv.convert(file, "pdf", function(
err,
result
) {
if (err) {
reject(err);
}
let File = file.substring(file.lastIndexOf("/")+1,file.lastIndexOf("."));
// result is returned as a Buffer
fs.writeFile(__dirname+"/files/converted/"+File+".pdf", result, error => {
/* handle error */
if (err) reject(error);
else resolve("./files/converted/"+File+".pdf");
});
});
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc(file);
console.log(res);
The return value of an async function is a promise, so naturally that's what your console.log outputs. You need to either consume the result via await (within another async function) or use then/catch (within another async function).
This is what you're currently doing:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
let res = myfunc("some file");
console.log(res);
You need to be doing either this:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
(async() => {
try {
let res = await myfunc("some file");
console.log(res);
} catch (e) {
// Deal with the fact there was an error
}
})();
or with then and catch:
function convertFiletoPDF(file) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 400, "Done");
});
}
async function myfunc(file){
let res = await convertFiletoPDF(file);
return res;
}
myfunc("some file")
.then(res => {
console.log(res);
})
.catch(e => {
// Deal with the fact there was an error
});
convertFiletoPDF()
This function run and returned a Promise. This is fine.
myfunc()
Lets say myfunc takes 10 seconds. Javascript starts to wait newly created thread result from libuv via event loop mechanism. So, Javascript says, "That one is async, I will not wait, when it finishes it will let me know and i will run my then callback and then I will proceed with its output."
Javascript keeps his promise. Tries to run next below lines. myFunch is still working. Output is not ready yet. Returns undefined.
let res = myfunc(file);
console.log(res);
You get undefined.
Someone might find this example from my code useful. You can wrap it in a promise and then resolve the custom promise and then call another promise to confirm the receipt of the original web3 call.
return new Promise((resolve, reject) => {
tokenContract.methods.approve(
exchangeAddress,
BIG_NUMBER_1e50
)
.send({ from })
.once('transactionHash')
.once('receipt', receipt => resolve(receipt))
.on('confirmation')
.on('error', err => reject(err))
.then( receipt => // will be fired once the receipt its mined
console.log(receipt),
);
});

Categories

Resources