--- date: 2023-02-01 09:39:03+00:00 description: Why you shouldn't use async in your new Promise() mp-syndicate-to: - https://brid.gy/publish/mastodon - https://brid.gy/publish/twitter post_meta: - date tags: - nodejs - typescript - javascript - softeng title: Async Promise Constructors type: posts url: /2023/2/1/async-promise-constructors --- I ran into an interesting typescript/js problem yesterday at work. The following code snippet was generating an error and a stack trace that was never being propagated up to the caller: ```typescript return new Promise(async (resolve, reject) => { const data = await this.client.getObject(bucket, path); const buf: any[] = []; data.on("data", (data) => { buf.push(data); }); data.on("error", (err) => { reject(err); }); data.on("end", () => { resolve(JSON.parse(Buffer.concat(buf).toString())); }); }); ``` The code basically uses [minio](https://min.io/)'s client library to get the contents of an object in an S3 storage bucket and then wraps the stream that it returns in a promise that will eventually parse the content of the S3 object into a JS object or reject if something went wrong. Seasoned js folks may have already spotted the problem (or at least guessed it based on the blog post title). I'ved used an `async` function inside my Promise constructor. I'm doing this so that I can make my async call to S3 but this has the added side effect of turning my promise constructer itself into a promise (promise-ception?). Promise constructors can't `await` so effectively if the `getObject` async call fails, the error is lost and nothing happens and the outer promise is never resolved or rejected. As it turns out, [eslint has a rule for this](https://eslint.org/docs/latest/rules/no-async-promise-executor) which I had turned off. ### Solution I could make the getObject outer function async and then do the `getObject()` call in the outer layer: ```typescript async function someFunction() { const data = await this.client.getObject(bucket, path); return new Promise((resolve, reject) => { const buf: any[] = []; data.on("data", (data) => { buf.push(data); }); data.on("error", (err) => { reject(err); }); data.on("end", () => { resolve(JSON.parse(Buffer.concat(buf).toString())); }); }); } ``` Now if the `getObject` call fails the whole `someFunction()` fails (effectively the promise that is generated by use of the await/async syntactic sugar is rejected).