GithubHelp home page GithubHelp logo

scopsy / await-to-js Goto Github PK

View Code? Open in Web Editor NEW
3.2K 22.0 152.0 219 KB

Async await wrapper for easy error handling without try-catch

Home Page: http://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/

License: MIT License

JavaScript 19.34% TypeScript 80.66%
async async-await es2017 try-catch

await-to-js's Introduction

await-to-js

NPM version Downloads

Async await wrapper for easy error handling

Supported by:
The open-source notification infrastructure

Pre-requisites

You need to use Node 7.6 (or later) or an ES7 transpiler in order to use async/await functionality. You can use babel or typescript for that.

Install

npm i await-to-js --save

Usage

import to from 'await-to-js';
// If you use CommonJS (i.e NodeJS environment), it should be:
// const to = require('await-to-js').default;

async function asyncTaskWithCb(cb) {
     let err, user, savedTask, notification;

     [ err, user ] = await to(UserModel.findById(1));
     if(!user) return cb('No user found');

     [ err, savedTask ] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
     if(err) return cb('Error occurred while saving task');

    if(user.notificationsEnabled) {
       [ err ] = await to(NotificationService.sendNotification(user.id, 'Task Created'));
       if(err) return cb('Error while sending notification');
    }

    if(savedTask.assignedUser.id !== user.id) {
       [ err, notification ] = await to(NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you'));
       if(err) return cb('Error while sending notification');
    }

    cb(null, savedTask);
}

async function asyncFunctionWithThrow() {
  const [err, user] = await to(UserModel.findById(1));
  if (!user) throw new Error('User not found');
  
}

TypeScript usage

interface ServerResponse {
  test: number;
}

const p = Promise.resolve({test: 123});

const [err, data] = await to<ServerResponse>(p);
console.log(data.test);

License

MIT © Dima Grossman && Tomer Barnea

await-to-js's People

Contributors

ciriousjoker avatar combarnea avatar danielsogl avatar gomesalexandre avatar jaredmcateer avatar jpeer264 avatar nicholaiii avatar rdsedmundo avatar scopsy avatar slaweet avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

await-to-js's Issues

TypeError: to is not a function

When using this library, I needed to import with the following pattern:

import { to } from "await-to-js";

Instead of the approach in the README:

import to from "await-to-js".

Otherwise I would get an error:

TypeError: to is not a function

From adding logging to the library, it appears that the import statement is using await-to-js.umd.js and not await-to-js.es5.js.

Tested with Node v14.17.0 running as node index.mjs.

Q: Usage with AsyncIterators?

Hello,

first of all, thank you very much for your work. Await-to-js is very useful!

I have a question for usage of await-to-js with AsyncIterators:

# import { DataMapper } from "@aws/dynamodb-data-mapper";
for await (const foo of this.dataMapper.query(EntityModel, { ... })) {
    ...
}

It is possible to use 'to' also in this situation?

Why try/catch blocks in async/await in the first place?

Instead of try{ await promise } catch (e) {errorHandler(e)} , we can do await promise.catch(errorHandler). It's simpler and it uses the reject/resolve states mechanism of promises. Is there a use case where this would not work?

Type error when used with Bluebird

I'm using sequelizejs

	[err, dbResponse] = await to(
		user.findOne({
			where: {
				email: req.body.email,
				passwordHash: req.body.password,
			},
		}),
	);

err:

Argument of type 'Bluebird<{}>' is not assignable to parameter of type 'Promise<{}>'.
Property '[Symbol.toStringTag]' is missing in type 'Bluebird<{}>'.

ESLint prints a warning if error isn't used.

Hello everybody! I have a problem with your cool library.  When i wrap my Promise in the function, i need to get only response, without error. Like that:
const [_, res] = await to( this.rootStore.apiStore.request({ url: URLS.SIGN_IN, }), ); if (!res) return false;

But my ESLint prints a warning:
'_' is assigned a value but never used

I expect this error but I didn't understand how to properly handle it.
To improve this library, I'm ready to suggest you use Object as returned value instead of Array.
And I mean this should work like that:
const { error, data: response, } = await to(fetch('some-api'));

In this case, it will be possible to use one of the returned values without problems and also to save the ability to custom name variables.

I created PR about this issues - #37

Runs twice

Very simple example:

import { to } from "await-to-js";
import 
export async function test(
 endpoint, payload
) {
  console.log("once");
  let err, response;
  [err, response] = await to(myfunction(endpoint, payload));
  console.log("twice"); // this occurs twice
}

The last console.log will happen twice. It should only occur once.

typescript definitions

hi,

it would be nice to have typescript definitions of this library in order to preserve intellisense when using await-to.

UnhandledPromiseRejectionWarning on Promise error

Hi!
First of all, thanks for your package, I'm refactoring my code and it's been handy to make it more clear.

But I have an issue, when the promise resolves, everything goes ok, but when the promise fails, I get an error:

(node:52113) UnhandledPromiseRejectionWarning: TypeError: arr[Symbol.iterator] is not a function
at _iterableToArrayLimit (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/@babel/runtime/helpers/iterableToArrayLimit.js:8:39)
at _slicedToArray (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/@babel/runtime/helpers/slicedToArray.js:8:33)
at _callee2$ (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/src/controllers/auth.controller.js:72:26)
at tryCatch (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:62:40)
at Generator.invoke [as _invoke] (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:288:22)
at Generator.prototype.(anonymous function) [as next] (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:114:21)
at asyncGeneratorStep (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
at _next (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/@babel/runtime/helpers/asyncToGenerator.js:25:9)
at propagateAslWrapper (/Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/async-listener/index.js:504:23)
at /Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/async-listener/glue.js:188:31
at /Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/async-listener/index.js:541:70
at /Users/israelmuca/Software/Dev/GitHub/techcrm-backend/node_modules/async-listener/glue.js:188:31
at process.internalTickCallback (internal/process/next_tick.js:77:7)
(node:52113) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 9)
(node:52113) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Flow Typings

Frist of all: This is an awesome helper library!

I tried to figure out how to add typings, but I failed 😅 . Would be awesome if a flow pro could create typings and share them here. Since this package will not change significantly in the future it could also be legit to add them to the npm package with the .js.flow extension.

Wrong typings on successful response

Hello.

I noticed that you're declaring the value of the Promise in case of success as allowed to be undefined per default, and this is wrong.

export declare function to<T, U = any>(promise: Promise<T>, errorExt?: object): Promise<[U | null, T | undefined]>;

What I meant is the T | undefined. This | undefined makes it necessary to check if the successful result is defined, when we already know it's. i.e:

    const [
      downloadError,
      object,
    ] = await to(this.s3.getObject({ Bucket: bucket, Key: key }));

    if (downloadError) {
      return this.throwError(`Unable to download ${bucket}/${key} due to: ${downloadError}`);
    }

    const supportedMimeTypes = ['image/png', 'image/jpg'];

    /* It's saying here that 'object' may be undefined. But we know it isn't as there's no error. */
    if (!supportedMimeTypes.includes(object.ContentType)) {
      return this.throwError(`Unsupported image mimetype: ${object.ContentType}`);
    }

Those typings from this repository, that I remember that had a Pull Request here were more correct: https://github.com/phra/await-to-ts/blob/master/index.ts

i.e: if we know that it could be undefined, we should pass it by ourselves, like await to<[Error, string | undefined]> and not having the library assuming it by default itself.

Do we even need reject?

Do we need the reject callback inside a promise?
The below code seems to be working out for me, it catches any error and instead of rejecting it returns null in the resolve function.
Is this bad code, if so why?

function fecthDB() {
	return new Promise((resolve, reject) => {
		try {
			db.query('SELECT * FROM users' (err, result) => {
				if (err || !results) {
					return resolve(null)
				}
				resolve(result)
			})
		} catch (e) {
			resolve(null)
		}
	})
}

function async getUsers() {
	var users = await fecthDB()
	if (!users) { // error handling
		return console.log('error')
	}
	console.log(users) // got users
}

😎-er Name: await-try-catch

I love this lib, but the name is just not as clean as the awesome functionality it provides.

So my proposed re-naming is "await-try-catch" and you could change the function from to() to tc() "tc" for try catch of course.

import tc from "await-try-catch";

const [error, response] = await tc(fetch("/cool"));

It should make the use case more clear to newcomers and although this lib doesn't use a literal try-catch, this lib does replace the use of try-catch in your async code so it's related in that sense.

Most importantly I think naming can be important for adoption and I want everyone to use this so I stop having to deal with actual try-catch blocks or worse .then in other people's code.

What do you think? Give me some thumbs: 👍 or 👎

Why version 3?

I see that a new major version was created, but I don't see any reason why this was done, according to this commit history.

Promise.all

What about Promise.all how would you do concurrency?

Typescript Error During Build

I'm using await-to-js in my REST api and when building out the project I'm getting a typescript error. I'm wondering if this is something in my code that is causing this error:

FIXED: my tsconfig.js file was the issue...

Doesn't work with "thenable" non-promises

firebase.storage.UploadTask has .then and .catch along with some other things, but it's not a Promise, so your function complains because its expecting a Promise and something that behaves "like" a promise just isn't enough.

Error while using KnexJS

let [err, data] = await to(
    knex('temp').insert({
        // some code
    })
  )
"Argument of type 'QueryBuilder' is not assignable to parameter of type 'Promise<any>'.
  Property '[Symbol.toStringTag]' is missing in type 'QueryBuilder' but required in type 'Promise<any>'."

I checked some closed issues and it seems that #21 had solved this. I have checked the commit history and now it seems to have reverted back to Promise<T>.

So any workaround for this scenario?
@scopsy

Allow type fuzziness for the 'promise' parameter

I personally find the await fuzzy type handling helpful in some cases. It would be nice to preserve that behavior with to(), and allow non-promise types to "pass through".

eg.

async function fuzzy(thing) {
    const res = await to(thing)
    console.log(res)
}
const prom = new Promise(r => r(1))
fuzzy(prom)
fuzzy(1)

// > 1
// > 1 (instead of throw)

Right now when invoking to(1) it ends up calling 1.then(...) expecting it to be a promise

#15

Why use the word "to"?

Scopsy, thank you for thinking about and productionizing something like this. I actually stumbled upon await-to-js when I was handling my own try/catch messiness with async/await. I too am a fan of the go way of handling errors (which might change in Go2 to my dismay). I just have one question and that is why use the word "to"? I understand that the module fundamentally uses the callback on a promise's then to nicely inline err and response, but how does "to" inform that procedure?

Thanks!

Add named export for more convenient usage for applications using ES modules

Problem

Currently example usage (written for CommonJS modules) doesn't work for ES modules (aka ESM where application has "type": "module" in package.json), because default exports don't play nice with ESM.

This affects both TypeScript and vanilla JS (with ES, not CommonJS modules), but if you'd use TypeScript, you would get a compile-time error when using like in the example from readme:

import to from 'await-to-js'
...
const [err, data] = await to<ServerResponse>(p)
src/main.ts:9:27 - error TS2349: This expression is not callable.
  Type 'typeof import("/home/ats/proj/easypark/productivity/github-patcher/node_modules/await-to-js/dist/types/await-to-js")' has no call signatures.

9 const [err, data] = await to<ServerResponse>(p)
                            ~~

Workaround

To work around it, you would need to replace to( with to.default(, like this:

const [err, data] = await to.default<ServerResponse>(p)

This is nasty - it requires different usage for ESM and CommonJS (can't use the same approach for both module systems).

ESM compatible nicer solution

Instead of exporting only default export (that would keep backwards compatibility for CommonJS), you could also add named export for the same function, so that applications, that use ES modules could use

import { to } from 'await-to-js'
const [err, data] = await to(p)

(that also would work for CommonJS modules)
instead of the default import + workaround mentioned above:

import to from 'await-to-js'
const [err, data] = await to.default(p)

No default error when promise.reject() no params

Hi,
the await-to-js readme does not mention the different between using method 1(try catch) and method 2(await-to-js).
but their use in reject is different.

For example:

function returnPromise() {
    return Promise.reject();
}

1.when use try catch:

try {
   const res = await this.returnPromise();
   console.log("res", res);
   console.log("first case normal process");
 } catch (err) {
   console.log("err", err);
   console.log("first case abnormal process");
 }

err is undefined,but "first case abnormal process" can output normally.

2.when use await-to-js

const [err, res] = await to(this.returnPromise());
 if (res) {
   console.log("res", res);
   console.log("second case normal process");
 } else if (err) {
   console.log("err", err);
   console.log("second case abnormal process");
}

err is undefined, so "second case abnormal process" cannot output normally.

Expect:

if await-to-js error is undefined, return default error(new Error() or other).

Does not work with Promise.promisify

Using

  • node v12.13.1
  • yarn 1.19.2
  • Windows 10

The following code should return [undefined, [Object object]]

const Promise = require("bluebird");
const WebTorrent = require('webtorrent');

// Create the torrent client
const client = new WebTorrent();

// Make promises
client.addAsync = Promise.promisify(client.add);

const [err, data] = await to(client.addAsync(magnet, { path: pathToSave }));

In this example, err is the data i want, and data is undefined......

promise.reject() no params error always undefined

Demo

function delay(time = 0) {
	return new Promise((reslove, reject) => {
			if (time > 2000) {
				reject();
			} else {
			reslove(time);
		}
	});
};
const [error, data] = await to(delay(2100));
console.log(error, data); // error always undefined

This is just a simple example

Why

There are many UI frameworks. Confirm uses project, and reject does not return parameters

Typescript Typings To Use Conditional Logic

Hello, I wanted to reference a previously closed issue: #14

I just tested the following code and was still getting type errors:

const waiter = (): Promise<boolean> => Promise.resolve(true);

const test = async (): Promise<boolean> => {
  const [err, res] = await to<boolean>(waiter());
  if (err) {
    return false;
  }

  return res;
};

Typescript complains that res may be undefined. But since we have handled the error case we know that res is defined.

Thank you

commonjs require

Using just nodejs 8.6 (not packed for browser nor using babel)
after poking around in the dist folder I came up with this.

to = require('await-to-js').to

returns

function to(promise, errorExt) {
    return promise
        .then(function (data) { return [null, data]; })
        .catch(function (err) {
        if (errorExt) {
            Object.assign(err, errorExt);
        }
        return [err, undefined];
    });
}

so seems correct

You probably should put that in this readme as now nodejs supports async await natively, but still needs commonjs requires.

Every CATCH has an IF

I almost love this library. I hate the second argument.

Every catch will result in running an if condition. Just too costly for my blood.

I looked at await-of and it has no IF but it returns with the value first and the error second. Hate that more but it has no inner IF logic.

I looked at await-on and thats just a no no no no.

Would you accept a pull request if I added another function that does all the same EXCEPT for no second argument and no catch IF condition?

Weird issue with pre-commit hook

I getting weird error when git committing on my fork:

$ git commit
husky > npm run -s precommit (node v10.3.0)

 ❯ Running tasks for {src,test}/**/*.ts
   ✖ prettier --write --no-semi
     → cosmiconfig requires at least version 4 of Node, please upgrade
     git add
✖ prettier --write --no-semi found some errors. Please fix them and try committing again.

**cosmiconfig requires at least version 4 of Node, please upgrade**


husky > pre-commit hook failed (add --no-verify to bypass)

Says cosmiconfig wants atleast node v4, but I'm on node v10

Anyone else?

Using "to", still getting "Unhandled promise rejection", why?

Hi, I tried your repo in my little crypto project

I use ccxt:

const ccxt = require('ccxt')
let log = require('electron-log')
let to = require('await-to-js')

async withdraw(currency, amount, address, tag = null) {
      let exchange = new ccxt.bitstamp(myApiKey)
      let error, response
      [error, response] = await to(exchange.withdraw(currency, amount, address, tag))
      log.info(error)
      log.info(response)
      return response || error
}

When I call my withdraw function and I try to withdraw 5 ripple (and I know it should generate an error), I still get:

(node:18252) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): TypeError: to is not a function
(node:18252) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:18252) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): Error: bitstamp {"status":"error","reason":{"amount":["Ensure this value is greater than or equal to 20."]}}

Ideally I'd like to return either the response or the error.

Ideas why this doesn't work? Looking at your documentation I was expecting this repo to handle the errors. Thanks for any help.

Commit 6c25d6a leads to errors if no return type was defined

Commit 6c25d6a (Update 2.0.1 to 2.1.0) leads to type errors, if no promise type was passed.

Previous behaviour:
Not passing any type worked, because type of promise resolve was defined as possibly undefined.
v2.0.1: src/await-to-js.ts:9

): Promise<[U | null, T | undefined]> {

let error: Error;
let user: User;
[error, user] = await to(...);

Current behavior:

TS2322: Type '{}' is not assignable to type 'User'.   Property 'userId' is missing in type '{}'.

The user is enforced to pass a type, because no default is set.

let error: Error;
let user: User;
[error, user] = await to<User>(...);

This is nonsense, because the correct way is to inherit the type of the passed promise as return type, and not define it again with to<T>(). This way, the type check will work properly, and users were not enforced to define all types on update. 2.1.0 has breaking changes compared to 2.0.1
The user will still able to override the promise type by passing the new type with to<NewType>(). I'm not sure if this use case really exists.

To fix this:
src/await-to-js.ts:7

promise: any,

should be changed to
promise: Promise<T>,

If you agree, I can open a pull request.

Error not set correctly

Hey all, perhaps I'm not understanding something here

Using to
const [err, data] = await to(fetch('http://httpstat.us/500')); console.debug(err, data)

Result:
image

Shouldn't a 500 with a response.ok = false give something in err?

How to use this with mongoose's Query type?

One of the most important things the await-to-js solves for me is to be able to wrap the mongoose queries.
In the recent mongoose update, I am facing a type error.

The code:

const [err, dbUser] = await to(
     User.findOne({ email: this.email }).select(["_id", "name"]).lean()
);

And that shows this error:
image
If I am not wrong, it boils down to the fight between "Query" and "Promise". I see in the await-to-js' code, it has a Promise wrapper type in the return (makes sense).
I am a complete newbie, so maybe I am doing something wrong.

Can anyone of you help me point at the right direction?
Thank you very much.

Not catching sync errors

The following code raises an unhandeled exception

const to = require('await-to-js').default

function p() {
    throw Error('ciao')
    return Promise.resolve('9')
}

async function main() {
    const [err, data] = await to(p())
    console.log(err.message, data)
}

main()

This works because the error is thrown inside a promise

const to = require('await-to-js').default

async function p() {
    throw Error('ciao')
    return Promise.resolve('9')
}

async function main() {
    const [err, data] = await to(p())
    console.log(err.message, data)
}

main()

It would be cool to pass a callback to catch these errors

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.