Written below is an example of my code I usually do.
What I did is..
Add try-catch for a promise function with async-await.
Do no add try-catch for a promise function without async-await.
What I want to know is my code is alright, and it is anti-pattern or not.
Thank you in advance.
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
try {
const category = await CoreStatus.findAll()
resolve(category)
} catch(err) {
reject(err)
}
})
}
// Promise without Await
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
CoreStatus.findAll()
resolve(category)
}
})
}
What I want to know is my code is alright, and it is anti-pattern or not.
The first one will work properly, but is an anti-pattern.
The second one will not work properly.
Neither of the code blocks you show is the recommended way to do things because they are both needlessly wrapping an existing promise in another manually created promise. This is referred to as an anti-pattern. This first version will actually work properly, but it contains a bunch of useless code (thus making it an anti-pattern) and with a little more complexity in the function, it's very easy to make coding mistakes (which is why, in addition to the useless code it contains, its an anti-pattern).
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
try {
const category = await CoreStatus.findAll()
resolve(category)
} catch(err) {
reject(err)
}
})
}
It can instead be this:
const readStatusAll = data => {
return CoreStatus.findAll();
}
The caller will receive the promise as the return value and can then use either .then() and .catch() or await and try/catch. Since you aren't doing anything with the error, other than propagating it, you don't need to catch the error locally - you can just let it propagate back to the caller.
Your second version is just not correct at all:
// Promise without Await
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
CoreStatus.findAll()
resolve(category)
}
})
}
Because you're not paying any attention to any asynchronous return from CoreStatus.findAll() so you will resolve this manual wrapper promise long before the database call is actually done. In fact, this isn't even legal code as you have improper bracing.
Perhaps you meant to call resolve(category) in some callback or .then() handler associated with CoreStatus.findAll(). But, even if you did that, you still wouldn't be propagating errors back to the caller. This is not the way to do things.
Related
I have a function that needs to be executed. I used the GLTF parser to retrieve the vertices and faces. However it does not execute in the right time. This is the function that I currently have.
public parseGltfBlobs(toAdd: asmTreeNodeScene[]) {
const loader = new GLTFLoader();
for (let i = 0; i < toAdd.length; i++) {
let componentId: number = toAdd[i].componentId;
let isPart: boolean = toAdd[i].type == "part";
// Handle surface mesh requests for parts for server endpoint in json format
if (isPart) {
let surfaceMeshKey: string = componentId.toString() + "." + this.asmStateServ.decimation.toString();
// Grab the byte array
let gltfBytes = <ArrayBuffer>this.assemblyFetchServ.surfaceMeshesGltf.get(surfaceMeshKey)['gltfBytes'];
return new Promise((resolve, reject) => {
loader.parse(gltfBytes, "", gltf => {
let mesh;
let vertices;
let faces;
mesh = <THREE.Mesh>gltf.scene.children[0];
vertices = mesh.geometry.attributes.position.array;
faces = mesh.geometry.index.array;
let surfaceMesh: Object = {
"component_id": componentId,
"vertices": vertices,
"faces": faces,
}
resolve(this.assemblyFetchServ.surfaceMeshes.set(surfaceMeshKey, surfaceMesh));
console.log("Stored data in surfaceMeshes map")
// Emit finished event
}
)
})}
}
}
The function is being called in the following way.
this.requestMissingResources(toAdd, this.isGLTF).subscribe(
() => {
},
(err) => {
console.log(err);
},
async () => {
if(this.isGLTF) {
console.log("Parsing gltf blobs");
await this.parseGltfBlobs(toAdd);
console.log("End of parsing gltf blobs")
}
However, I still do not receive the output promise. I was wondering why that is and what I am doing wrong out here? It would mean a lot if the correct code is given to me as that would help me out a lot as I am very new to this.
Moving answers from comments.
Looks like your promise doesn't have/call the resolve callback to even know it is finished.
When creating a promise define callbacks to mark it as completed successfully or failed. You use resolve and reject callbacks for it as so:
new Promise((resolve, reject) => { ... do something ... resolve(data); })
I don't see anything receiving the result of the promise anyway, nor returning any results from the promise. If you're not planning on returning any info from promise at least call resolve with a boolean in it to mark it as completed for you to know if you can proceed further or not.
The resolve(data) or reject(data) will help you correctly handle the promise. Try/catch works on catching these outputs.
try {
// if promise resolved successfully you will get data in the data variable
const data = await function_returning_a_promise();
}
catch (error) {
// if promise rejected you will catch an error here
console.error(error);
}
use try/catch to handle promises with await as above.
Is there anything asynchronous that you require a promise to parse it? Is this.assemblyFetchServ.surfaceMeshes.set asynchronous? If so you probably should wait for it to complete as well with another await. And if not you probably don't even need a promise at all.
So what I mean is:
const result = await this.assemblyFetchServ.surfaceMeshes.set(surfaceMeshKey, surfaceMesh);
If that's an asynchronous function you should wait for it to complete first. And if it is not you probably shouldn't even use promises in the first place.
I'd recommend reading through https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise carefully. Back in time it helped me a lot to understand promises myself.
With promises, is it bad practice to leave out the use of reject? If I just want to return a Promise to ensure this function is run in it's entirety and not block whatever follows this function.
function testFunc() {
return new Promise((resolve,reject) => {
// do stuff
resolve(true);
})
}
testfunc().then(() => { // additional stuff});
If there is a chance of the do stuff throwing an error, then you should make sure to use reject and call it in case there's an error, so that the caller can see that there was a problem and (hopefully) handle it appropriately. For example, you wouldn't want to do:
function testFunc() {
return new Promise((resolve,reject) => {
getApi((error, result) => {
resolve(result);
});
});
}
but instead
function testFunc() {
return new Promise((resolve,reject) => {
getApi((error, result) => {
if (error) reject(error);
resolve(result);
});
});
}
If there's really no chance of the contents of the function erroring (barring logic bugs that could be unknowingly present in any code), then reject won't help you, because you don't really have any place suitable to put it. For example:
function someWeirdPromiseFunctionThatBlocks() {
return new Promise((resolve) => {
for (let i = 0; i < 1e5; i++) {
}
resolve();
});
}
Note that if you just want to:
not block whatever follows this function.
then just returning a Promise won't accomplish that, because the Promise constructor will run all of its code synchronously until it encounters something asynchronous (like getApi). To avoid blocking the running of sync code below, use Promise.resolve or setTimeout, eg:
function testFunc() {
return Promise.resolve()
.then(() => {
// do other stuff
});
No, its not necessary at all. reject is meant to be used in a similar way as one might use throw - it's for errors that happen.
In fact, if you're familiar with javascript's async/await syntax, this async function...
async function doSomething() {
throw new Error('Oh no!')
}
...roughly translates to this:
function doSomething() {
return Promise.reject(new Error('Oh no!'))
}
which shows how reject is intended to be used wherever you might normally throw an error.
Here's an example promise that I use often where there isn't any "reject" that would make sense:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
wait(1000).then(() => console.log('done'))
Many others such examples exist.
Is there any typescript config option or some workaround to check if there is no resolve called in new Promise callback?
Assume I have a promise
new Promise(async (resolve, reject) => {
try {
// do some stuff
// but not calling resolve()
} catch (e) {
reject(e)
}
})
I want typescript to warn me that I did not call resolve(). Is it possible?
I know that I can use noUnusedParameters, but there are a couple of situations where I still need unused parameters (e.g. request inside of express.Hanlder, where I only use response, etc.)
No, that is not possible. Knowing whether code calls a certain function (resolve in this case) is just as hard as the halting problem. There is proof that no algorithm exists that can always determine this.
To illustrate, let's assume that the algorithm for determining whether a function calls resolve exists, and is made available via the function callsResolve(func). So callsResolve(func) will return true when it determines that func will call resolve (without actually running func), and false when it determines that func will not call resolve.
Now image this func:
function func() {
if (!callsResolve(func)) resolve();
}
... now we have a paradox: whatever this call of callsResolve returned, it was wrong. So for instance, if the implementation of callsResolve would have simulated an execution of func (synchronously) and determines that after a predefined timeout it should return false, the above is a demonstration of a function that calls resolve just after that timeout expired.
The closest you can get to a compile time check is to use async / await syntax.
If you don't want to use that, you could timeout your promises, though you would have to do that with each of your promise after / when you are creating them.
A solution could look like this:
export const resolveAfterDelay = (timeout: number) => new Promise((r) => setTimeout(r, timeout));
export const rejectAfterDelay = async (timeout: number) => {
return new Promise((resolve, reject) => setTimeout(() => reject(`Promise timed out as resolve was not called within ${timeout}ms`), timeout));
};
export const timeoutPromise = <T>(timeout: number) => async (p: Promise<T>): Promise<T> => {
return Promise.race([p, rejectAfterDelay(timeout)]);
};
const timeoutAfter1s = timeoutPromise(1e3);
const timeoutAfter10s = timeoutPromise(10e3);
timeoutAfter10s(resolveAfterDelay(3e3)).then(success => console.log("SUCCESS IS PRINTED")).catch(console.error); // works
timeoutAfter1s(resolveAfterDelay(3e3)).then(success => console.log("NEVER REACHED")).catch(console.error); // aborts
const neverResolvingPromise = new Promise(() => {
});
timeoutAfter1s(neverResolvingPromise).catch(console.error); // promise never resolves but it will be rejected by the timeout
It makes use of Promise.race. Basically, whatever first resoves or rejects will be returned. We want to always reject a Promise if it does not resolve in time.
You would always have to wrap your Promise on creation like
timeoutAfter10s(new Promise(...));
And you would have to adapt the timeout according to your use case.
Although I do realize what the error means and why it happens, I think I have a use case that goes outside the expected. I am using Word.run() inside another promise, like so:
return new Promise((resolve, reject) => {
window.Word.run(context => {
// do stuff with context
resolve(someData);
});
});
So, if I understood it correctly, this resolves my promise, but leaves the .run method hanging since there's no return context.sync() at the end? Or did I get it wrong? If I'm right, how can I rewrite the example above to keep .run working properly?
If you want your promise to resolve after the context syncs...
return new Promise((resolve, reject) => {
window.Word.run(context => {
// do stuff with context
return context.sync().then(function() {
resolve(someData); // promise will resolve after sync resolves
});
});
});
If you don't need to resolve after but just want to sync the context at some point in the future, you can actually do this and it won't wait for the sync:
return new Promise((resolve, reject) => {
window.Word.run(context => {
// do stuff with context
resolve(someData); // this will resolve the promise
return context.sync(); // this will actually still happen since the function never returns
});
});
There seems something inherently wrong with having to define a Promise's callback as asynchronous:
return new Promise(async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
This is apparently an antipattern and there are coding problems which can arise from it. I understand that it becomes easier to fail to catch errors here, even when placing await statements inside try/catch blocks.
My first question is, what's the best way to code something like this, when one wants to forward a Promise with different resolve/reject values? With then/catch? I.e.
return new Promise((resolve, reject) => {
somethingAsynchronous().then(value => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}); // errors would now be propagated up
});
Or do you just take it out the Promise constructor altogether as suggested here?
async function outerFunction() {
const value = await somethingAsynchronous();
return new Promise((resolve, reject) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
}
But what if you have several await statements in the outerFunction(), i.e. a linear code block calling several asynchronous functions. Would you then have to create and return a new Promise every time?
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
return 'Nope. Try again.' // another reject case
}
const value = await somethingAsynchronous();
// ...
}
I have the feeling that I'm making this more complicated than it should be. I'm trying to avoid nesting callbacks/chaining then/catch blocks without creating more problems in the future.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
You do this:
async function outerFunction() {
const value = await somethingAsynchronous();
if (value === something) {
return 'It Worked!';
}
throw Error('Nope. Try again.');
}
Using async wraps the result of outerFunction with a Promise.
If you want that wrapping promise to resolve to something, just return it from the async function. If you want the wrapping promise to be rejected, throw an error inside the async function.
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
throw Error('Nope. Try again.');
}
const value = await somethingAsynchronous();
// ...
}
new Promise(async (resolve, reject) => { ... }) is relatively new antipattern. It results in creating 2 promise objects instead of 1, uncaught errors that happen inside constructor cannot be caught with try..catch and result in unhandled rejection.
Considering that promise asynchronous code can be handled with async..await, current use case for Promise constructor is non-promise asynchronous code, e.g.:
new Promise(resolve => setTimeout(resolve, 1000))
When Promise constructor contains synchronous code or involves other promises, a promise should be constructed with async function. A drop-in replacement is async IIFE:
return (async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return 'It worked!';
} else {
throw 'Nope. Try again.';
}
})();
If the need for Promise constructor still presents when being used together with async, Promise constructor should be moved down in hierarchy so it won't wrap any async function.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
async function isn't just a function that is executed asynchronously, it returns another promise that is supposed to be utilized - or at least handled with catch. Promise isn't supposed to utilize a promise that is returned from constructing function.
The constructor can resolve on same tick and doesn't necessarily have to be asynchronous.
Promise.resolve(1);
is similar to
Promise(resolve => resolve(1))
and not to
Promise(resolve => setTimeout(() => resolve(1)))
You can also chain the promises yourself by simply doing this:
return new Promise((resolve, reject) => {
somethingAsynchronous().then((value) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}, (error) => { reject(error); });
});
I've been using this for some time and it works perfectly for me.