FeathersJS - How to cancel hook on error - javascript

In my registration service I've created a 'before' hook to check if the user is already registered to a tournament. If the user is found to be registered, I have it throwing an error.
Per feathersjs documentation:
If a promise fails, the error will be propagated immediately and will
exit out of the promise chain.
The problem I'm having is the error is being "triggered" and showing the user has already registered, but the registration still takes place. Meaning, the hook is never "exited".
Here is my function
exports.checkRegistered = () => {
return (hook) => {
let tourneyId = hook.data.tourneyId
let userId = hook.data.userId
return hook.service.find({
query: {
tourneyId: tourneyId,
userId: userId
}
}).then(reg => {
// User already registered
if (reg.data.length != 0) {
// Also tried to return a promise with same result.
// return Promise.reject(new Error('Already registered.'))
throw new Error('Already registered');
}
}).catch(err => {})
}
}
Here is my "before hook" object
exports.before = {
create: [
api.checkRegistered()
]
};

The problem is the .catch(err => {}). It will return a promise that just resolves successfully with undefined which will then continue the hook chain as if no error happened. Removing it should fix your issue.

Related

Node throws UnhandledPromiseRejectionWarning for Mongoose requests using Promise.all with .catch() statement

I'm new to Node/Mongoose and am trying to handle errors correctly in a script to add players to a league. In the below code, explicitly thrown and non-Promise related errors are caught correctly by the .catch() statement, but rejected Promises are not.
For example, trying to pass an invalid userID throws User not found.
But if I test Promise rejection by disconnecting the database, I get the following:
(node:6252) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): MongoNetworkError: failed to connect to server [localhost:27017] on first connect [MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017]
Am I using Promise.all() and .catch() incorrectly somehow?
Just to be clear, I'm trying to work out why the error isn't being handled, not why the error is being thrown.
My script:
const
mongoose = require('mongoose'),
User = require('./models/users'),
League = require('./models/leagues'),
dbUrl = process.env.DBURL || 'mongodb://localhost/predictor';
mongoose.connect(dbUrl, { useNewUrlParser: true });
const addUserToLeague = (userId, leagueId) => {
let foundUser = User.findById(userId);
let foundLeague = League.findById(leagueId);
return Promise.all([foundUser, foundLeague])
.then(arr => {
if(!arr[0]){
throw 'User not found';
}else if(!arr[1]){
throw 'League not found';
}
return arr;
})
.then(arr => {
arr[0].leagueMemberships.push(arr[1]);
arr[1].users.push(arr[0]);
return arr;
})
.then(updatedArr => {
updatedArr[0].save();
updatedArr[1].save();
return updatedArr;
})
.then(updatedArr => { console.log(`User ${updatedArr[0]._id} added to league ${updatedArr[1]._id}`) })
.catch(err => { console.log('Error:', err) });
};
addUserToLeague(process.argv[2], process.argv[3]); // Needs 2 args: User ID and League ID
As Bergi pointed out, the error would appear to be from connect, which returns a promise that you're not handling at all — including not waiting for it to finish. So at a minimum, you need to handle that:
const connectionPromise = mongoose.connect(dbUrl, { useNewUrlParser: true })
.catch(error => {
// Handle connection error
});
Then in addUserToLeague:
const addUserToLeague = (userId, leagueId) => {
return connectionPromise.then(connection => {
// ...logic here
});
};
...but, I question whether you should be connecting when the module is loaded like that, rather than passing a connection into addUserToLeague.
Aside from that, the actual use of Promise.all is okay, but:
One hopes that findById doesn't resolve the promise with a falsy value if the item isn't found, so that whole first then handler seems unnecessary.
Presumably save returns a promise. You're not handling rejection or waiting for resolution of those if so.
I'd use destructuring to avoid arr[0] and arr[1], as it's easy to forget the order.
There's no reason for the then handler with the push calls to be separate from the then handler doing the saving.
addUserToLeague should return the result of the promise chain, so that code calling it A) Knows when it's finished, and B) Knows when it fails.
Errors shouldn't be handled in addUserToLeague; instead, handle them in its caller.
There's also the issue that the data is denormalized: You're storing the membership information in both the user object and the league object. Maybe that's relatively normal in document databases (I wouldn't know); in an RDBMS you'd store the information in a single place. The reason is clear from the code in addUserToLeague: What if saving the user succeeds but saving the league fails? Then the user object says it's a member of a league the league object doesn't say it's a member of. There's also the problem that since it's stored in two places, even if nothing goes wrong, for a brief period one of the (the user or the league) will have been saved but the other won't have been. Both are integrity problems. If you can normalize it to storing this information in one place, that would be good. If you can't, you need to update the code so that it saves one of them, waits for that to succeed, saves the other, and if that fails attempts to undo the change to the first.
Something like this (I don't attempt to address the normalization issue here, that's a big picture thing):
const
mongoose = require('mongoose'),
User = require('./models/users'),
League = require('./models/leagues'),
dbUrl = process.env.DBURL || 'mongodb://localhost/predictor';
const addUserToLeague = (connection, userId, leagueId) => {
return Promise.all([
User.findById(userId),
League.findById(leagueId)
])
.then(([user, league]) => {
user.leagueMemberships.push(league);
league.users.push(user);
return Promise.all([user.save(), league.save()]);
})
.then((([user, league]) => {
console.log(`User ${user._id} added to league ${league._id}`);
});
};
mongoose.connect(dbUrl, { useNewUrlParser: true })
.then(connection => addUserToLeague(connection, process.argv[2], process.argv[3]) // Needs 2 args: User ID and League ID
.catch(error => {
// Handle/report error
});
If you're using any recent verson of Node, you can use an async function:
const
mongoose = require('mongoose'),
User = require('./models/users'),
League = require('./models/leagues'),
dbUrl = process.env.DBURL || 'mongodb://localhost/predictor';
const addUserToLeague = async (connection, userId, leagueId) => {
let [user, league] = await Promise.all([
User.findById(userId),
League.findById(leagueId)
]);
user.leagueMemberships.push(league);
league.users.push(user);
[user, league] = await Promise.all([user.save(), league.save()]);
console.log(`User ${user._id} added to league ${league._id}`);
};
mongoose.connect(dbUrl, { useNewUrlParser: true })
.then(connection => addUserToLeague(connection, process.argv[2], process.argv[3]) // Needs 2 args: User ID and League ID
.catch(error => {
// Handle/report error
});

How do I handle an error and then immediately break out of a promise chain?

So I have an Express app that uses middleware to parse JSON POST requests and then populate a req.body object. Then I have a promise chain that validates the data against a schema using Joi, and then stores it in a database.
What I would like to do is check if an error was thrown after one of these processes, handle it appropriately by sending a status code, then COMPLETELY ABORT the promise chain. I feel like there should be some EXTREMELY CLEAN AND SIMPLE way to do this, (perhaps some sort of break statement?) but I can't find it anywhere. Here is my code. I left comments showing where I hope to abort the promise chain.
const joi = require("joi");
const createUserSchema = joi.object().keys({
username: joi.string().alphanum().min(4).max(30).required(),
password: joi.string().alphanum().min(2).max(30).required(),
});
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
//CLEANLY ABORT the promise chain here
})
.then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
})
.catch(error => {
res.sendStatus(500);
//CLEANLY ABORT the promise chain here
})
//Only now, if both promises are resolved do I send status 200
.then(() => {
res.sendStatus(200);
}
)
});
You can't abort a promise chain in the middle. It's going to either call a .then() or a .catch() later in the chain (assuming there are both and assuming your promises resolve or reject).
Usually, the way you handle this is you put one .catch() at the end of the chain and it examines the type of error and takes appropriate action. You don't handle the error earlier in the chain. You let the last .catch() handle things.
Here's what I would suggest:
// helper function
function err(status, msg) {
let obj = new Error(msg);
obj.status = status;
return obj;
}
//Here begins my promise chain
app.post("/createUser", (req, res) => {
//validate javascript object against the createUserSchema before storing in database
createUserSchema.validate(req.body).catch(validationError => {
throw err("validateError", 400)
}).then(validatedUser => {
//accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(err => {
throw err("createUserError", 500);
});
}).then(() => {
// success
res.sendStatus(200);
}).catch(error => {
console.log(error);
if (error && error.status) {
res.sendStatus(error.status);
} else {
// no specific error status specified
res.sendStatus(500);
}
});
});
This has several advantages:
Any error propagates to the last .catch() at the end of the chain where it is logged and an appropriate status is sent in just one place in the code.
Success is handled in just one place where that status is sent.
This is infinitely extensible to more links in the chain. If you have more operations that can have errors, they can "abort" the rest of the chain (except the last .catch() by just rejecting with an appropriate error object).
This is somewhat analogous to the design practice of not having lots of return value statements all over your function, but rather accumulating the result and then returning it at the end which some people consider a good practice for a complicated function.
When debugging you can set breakpoints in one .then() and one .catch() to see the final resolution of the promise chain since the whole chain goes through either the last .then() or the last .catch().
.catch returns a resolved Promise by default. You want a rejected Promsise. So, you should return a rejected promise from inside the .catch, so that future .thens won't execute:
.catch(validationError => {
res.sendStatus(400);
return Promise.reject();
})
But note that this will result in a console warning:
Uncaught (in promise) ...
So it would be nice to add another .catch to the end, to suppress the error (as well as catch any other errors that come along):
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return Promise.reject();
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => void 0);
If you want to avoid all future .thens and future .catches, I suppose you could return a Promise that never resolves, though that doesn't really sound like a sign of a well-designed codebase:
const resolveAfterMs = ms => new Promise(res => setTimeout(() => {
console.log('resolving');
res();
}), ms);
console.log('start');
resolveAfterMs(500)
.then(() => {
console.log('throwing');
throw new Error();
})
.catch(() => {
console.log('handling error');
return new Promise(() => void 0);
})
.then(() => {
console.log('This .then should never execute');
})
.catch(() => {
console.log('final catch');
});
A cleaner solution for what you are trying to accomplish might be to use express-validation, which is a simple wrapper around joi that provides you with express middleware for validation of the body, params, query, headers and cookies of an express request based on your Joi schema.
That way, you could simply handle any Joi validation errors thrown by the middleware within your "generic" express error handler, with something like:
const ev = require('express-validation');
app.use(function (err, req, res, next) {
// specific for validation errors
if (err instanceof ev.ValidationError)
return res.status(err.status).json(err);
...
...
...
}
If you don't want to use the express-validation package, you could write your own simple middleware that does more or less the same thing, as described here (see example here).
One strategy is to separate your error handling in subpromises which have their individual error handling. If you throw an error from them, you'll bypass the main promise chain.
Something like:
return Promise.resolve().then(() => {
return createUserSchema.validate(req.body)
.catch(validationError => {
res.sendStatus(400);
throw 'abort';
});
}).then(validatedUser => {
// if an error was thrown before, this code won't be executed
// accepts a hash of inputs and stores it in a database
return createUser({
username: validatedUser.username,
password: validatedUser.password
}).catch(error => {
// if an error was previously thrown from `createUserSchema.validate`
// this code won't execute
res.sendStatus(500);
throw 'abort';
});
}).then(() => {
// can put in even more code here
}).then(() => {
// it was not aborted
res.sendStatus(200);
}).catch(() => {
// it was aborted
});
You can skip the Promise.resolve().then() wrapping, but it's included for illustrative purposes of the general pattern of subdividing each task and its error handling.

my graphql server mutation return null value

I am having challenges retrieving the results of my mutation. I need to create a db record and send an email notifying to user that the registration was successful. since both the sending of the email and the db update is server side I want to do both in the same mutation. If the email message fail the db must not be updated. So I have the following Mutation:
Mutation: {
createDealer(_, params) {
console.log("params: " + JSON.stringify(params));
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(params.dealer.password, salt, function(err, hash) {
// Store hash in your password DB.
console.log("hashed password " + params.dealer.password)
params.dealer.password = hash;
console.log("hashed password " + params.dealer.password + " Hash: " + hash);
let session = driver.session();
let query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d";
let here = "here".link("mymail#example.com");
let messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
return (sendEmail(messageObj))
.then(data => {
console.log('SendMail data....' + JSON.stringify(data));
return session.run(query, params)
})
.then(result => {
console.log('SendNeo4j data....' + JSON.stringify(result));
return result.records[0].get("d").properties
})
.catch((err) => {
console.log(err);
});
//});
});
}); // genSalt
} // Create Dealer
}, // Mutation
Even thought both actions are successful I can't seem to retrieve the results. I get 'undefined' for:
console.log('SendMail data....' + JSON.stringify(data));
while
console.log('SendNeo4j data....' + JSON.stringify(result));
does display the correct data
but graphiql returns 'null' for the mutate.
this is the graphiql mutation:
mutation CreateDealer($dealer: DealerInput!) {
createDealer(dealer: $dealer) {
email
name
}
}
with the DealerInput variables of course.
I have read where you can retrieve multiple results from a query/mutation but I am not sure how it works. Here I need both the results of the sendEmail and the db update for my Angular/apollo front-end....I would imaging graphiql knows nothing of the sendEmail but I expected it to return the properties I requested.
SendEmail:
module.exports = (message) =>
new Promise((resolve, reject) => {
const data = {
from: 'mymail#example.com',
to: message.to,
subject: message.subject,
text: message.text
};
mailgun.messages().send(data, (error) => {
if (error) {
return reject(error);
}
return resolve();
});
});
Can someone with a little more experience than I help me out here...thanks
Couple of things to fix here. Returning a Promise (or any other value) inside a callback doesn't do anything, and doing so won't let you chain additional Promises like you want. Instead, your promise gets fired off inside the callback and isn't awaited.
As a general rule of thumb, don't mix Promises and callbacks. If you absolutely have to use callbacks, always wrap the callback in a Promise (like you did inside sendMail). Luckily, most popular libraries today support both callbacks and Promises. Here's how you could refactor the code above to correctly chain all your Promises:
createDealer(_, params) {
return bcrypt.hash(params.dealer.password, 10) // note the return here!
.then(hash => {
params.dealer.password = hash
const session = driver.session()
const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
const here = "here".link("mymail#example.com")
const messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
return sendEmail(messageObj) // note the return here!
}).then(data => {
return session.run(query, params) // note the return here!
}).then(result => {
result.records[0].get("d").properties // note the return here!
})
bcrypt.hash will autogenerate the salt for you if you don't pass one in -- there's no need to call two separate functions
We kick off our Promise chain with bcrypt.hash, so we need to return the Promise it returns. A resolver must return a value or a Promise that will resolve to a value, otherwise it returns null.
Inside each then, we return a Promise. This way we "chain" our Promises, allowing the final value we return in the resolver to be the value the very last Promise in the chain resolves to.
We need to also fix your sendMail function to actually return the value. You're correctly returning the new Promise inside the function, but you also need to pass the returned data object to resolve. That tells the Promise to resolve to that value.
module.exports = (message) => new Promise((resolve, reject) => {
const data = // ...etc
mailgun.messages().send(data, (error) => {
if (error) reject(error) // no need to return, it's pointless
resolve(data) // pass data to resolve
})
})
Side note: looks like the official mailgun library supports Promises.
Additionally, I would strongly encourage you to look into using async/await, especially when dealing with a long Promise chain. It's less error prone and more readable:
createDealer async (_, params) {
const hash = await bcrypt.hash(params.dealer.password)
params.dealer.password = hash
const session = driver.session()
const query = "CREATE (d:Dealer {email:$dealer.email}) SET d += $dealer RETURN d"
const here = "here".link("mymail#example.com")
const messageObj = {
to: params.dealer.email,
subject: 'Dealer Registration',
text: `Thank you for signing up. To complete and activate your registration please click ${here}.`
}
const emailResult = await sendEmail(messageObj)
const result = await session.run(query, params)
return result.records[0].get("d").properties // still need to return!
}
EDIT: With regard to catching errors, GraphQL will catch any errors thrown by your resolver, which means you can often skip using catch yourself. For example, if your mailgun request fails, it'll generate some kind of error and your query will return null for data and the error details inside of the errors array.
That may be sufficient, although 1) you may want to log your error's stack elsewhere; and 2) in production, you probably don't want to expose internal error details to the public.
That means you'll probably want to use custom errors. As a bonus, you can add some custom properties to your errors to help the client deal with them eloquently. So your code may end up looking more like this:
class DeliveryFailureError extends Error {}
DeliveryFailureError.code = 'DELIVERY_FAILURE'
DeliveryFailureError.message = 'Sorry, we could not deliver the email to your account'
try {
await mailgun.messages.create()
} catch (err) {
logger.error('Mailgun request failed:', err.stack)
throw new DeliveryFailureError()
}

JS - Knex, Pass function to Transaction

I created a node.js application that uses the knex library to make database operations. The database is Microsoft SQL Server. I created a script called db.js that returns the knex object, and i have a controller.js script that makes the actually needed database operations. I have all the operations wrapped inside a translation statement, which brings me to my question. What i would like to do, is pass in a parameter that tells the transaction to rollback or commit. However, whenever i try to pass this function in, it just fails. Does anyone know if this feature achievable ? I was able to do this feature with the catch function.
I don't think its db specific, so anyone can download knex, hook it up a db and give it a shot with the code below.
example.js
/**
* #param {userID} Int
* #param {rollback} Boolean
*/
const getUsers = (userID, rollback) => {
// Using tran as a transaction object:
return db('master').transaction((tran) => {
db('master')
.select()
.from('users')
.where({ 'user_id': userID })
.transacting(tran)
.then(tran.rollback) // Works
// .then(transact(tran, rollback)) throws error
.catch((error) => {
logError(error, tran); // Works
});
// .catch(tran.rollback);
});
};
const logError = (error, transaction) => {
transaction.rollback;
console.log('transaction error: ',error);
console.log('transaction log: ',transaction);
};
const transact = (transaction, rollback) => {
try {
if (rollback) return transaction.rollback;
else return transaction.commit;
} catch (error) {
console.log(error);
}
};
const user = await getUsers(1, true); // error is thrown
assert.strictEqual(user.constructor === Array, true);
assert.strictEqual(user.length == 0, true);
Error Message
Error: the array [ {
"user_id": 1
"user_name": "JonnyBoy"
"zip": 1200
"email": "jjboy#test.com"
} ] was thrown, throw an Error :)
then takes a function as its first parameter. If the Promise resolves, the function that's passed to then is called with the value the Promise resolved to. The rollback property on the transaction object is a function so you can write:
someQuery().then(trans.rollback)
and rollback will be called when someQuery resolves. You could also write:
someQuery.then(result => trans.rollback(result))
These statements are equivalent.
That said, minimally, there's two changes that you need to make. One, fix how you're calling transact. For example:
.then(() => transact(tran, rollback))
And change how transact calls rollback:
const transact = (trx, rollback) => {
const action = rollback ? trx.rollback : trx.commit
return action()
}
Also bear in mind that rollback itself will return a rejected Promise with a generic error. You can pass a custom error to rollback and it will reject with that error instead.

Optional catch in javascript promises

The following is valid:
new Promise<void>((resolve, reject) => {
reject()
})
.then(() => {})
.catch(() => {})
But I might not always care about the error. Is there a way to make the catch optional?
I tried this but it didn't work:
new Promise<void>((resolve, reject?) => {
if (reject) reject()
})
.then(() => {})
Error: Uncaught (in promise): undefined
Is there a way to make the catch optional?
No. If you are using a promise that might error, you need to handle that (or propagate it to your caller).
Of course if you create the promise yourself, rejecting it is optional, and you can choose to never reject your promises so that you won't need to handle any errors. But if there are errors from promises that you are using, and you want to ignore them, you must do so explicitly. Just write
somePromise.catch(e => void e);
// or () => { /* ignore */ }
// or function ignore() {}
I was trying to solve the same issue, and finally come up with the following promise wrapper:
/**
* wraps a given promise in a new promise with a default onRejected function,
* that handles the promise rejection if not other onRejected handler is provided.
*
* #param customPromise Promise to wrap
* #param defaultOnRejected Default onRejected function
* #returns wrapped promise
*/
export function promiseWithDefaultOnRejected(customPromise: Promise<any>, defaultOnRejected: (_: any) => any): Promise<any> {
let hasCatch = false;
function chain(promise: Promise<any>) {
const newPromise: Promise<any> = new Promise((res, rej) => {
return promise.then(
res,
function(value) {
if (hasCatch) {
rej(value);
} else {
defaultOnRejected(value);
}
},
);
});
const originalThen = newPromise.then;
// Using `defineProperty` to not overwrite `Promise.prototype.then`
Object.defineProperty(newPromise, 'then', {
value: function (onfulfilled: any, onrejected: any) {
const result: Promise<any> = originalThen.call(newPromise, onfulfilled, onrejected);
if (typeof onrejected === 'function') {
hasCatch = true;
return result;
} else {
return chain(result);
}
}
});
return newPromise;
}
return chain(customPromise);
}
This function lets you wrap your promises with a defaultOnRejected function that will handle the rejected promise if no other handler is provided. For example:
const dontCare = promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {});
The result promise will never throw an "Unhandled Promise Rejection", and you can use it as follows:
dontCare.then(x=>console.log("never happens")).catch(x=>console.log("happens"));
or
dontCare.then(x=>console.log("never happens"), x=>console.log("happens"));
or simply without onRejected handler:
dontCare.then(x=>console.log("never happens")).then(x=>console.log("also never happens"));
An issue with this util is that it is not working as expected with async/await syntax: you need to propagate and handle the "catch" path as follows:
async () => {
try {
await promiseWithDefaultOnRejected(Promise.reject("ignored"), () => {})
.catch((e) => { throw e; });
} catch (e) {
console.log("happens");
}
}
You could resolve when the error is something you don't care about. If your catch returns anything other than a rejected promise, the error isn't propagated down the chain.
const ignorableError = new Error("I don't care about this error");
const myPromise = new Promise((resolve, reject) => {
reject(ignorableError);
})
.then(() => {})
.catch(error => {
if(error == ignorableError) {
console.log("Error ignored");
return;
}
// Do something else...
});
myPromise.then(() => console.log("Success"))
Let me try to describe your situation:
You have a service that gets user information and a function called getUser that uses that service. When the service fails for any reason then getUser does not have a user available. The result of getUser is used quite a lot of times in your code with the following situation(s):
User is available run a function (block of code).
User is not available run a function (block of code).
Run a function with the error/reject of the service.
When using getUser result you may want to run all 3 functions, a combination of 2 of them or only one.
Current getUser returns a Promise and this type does not seem to be suitable for your situation. Mainly because rejecting a promise and not catching it will cause unhandled promise rejection. And because if you want to run code if user is available or not it complicates the functions (they both have to check the result instead of assuming user is or is not available).
Maybe you can try the following code, please be careful making assumptions in the not available block, this could be due to any error. For example: it does not mean the user does not exist because it could be a network error.
In the example getUser is used but can be any function that returns a promise where you assume not available on reject.
const isAvailable = promise => {
//do not expose NOT_AVAILABLE outside this function
const NOT_AVAILABLE = {type:"not available"};
//no uncaught promise rejection errors by VM
const savePromise = promise.catch(x=>x);
return {
available:fn=>
promise
.catch(e=>Promise.reject(NOT_AVAILABLE))
.then(fn)
.catch(
e=>
(e===NOT_AVAILABLE)
? undefined//ignore
: Promise.reject(e)//re throw, error is not from service
),
//call not available with the promise if promise rejects
notAvailable:fn=>promise.catch(()=>fn(promise)),
catchError:promise.catch.bind(promise)
};
}
const service = arg =>
(arg===1)
? Promise.resolve(arg)
: Promise.reject(arg)
const getUser = arg => isAvailable(service(arg));
var user = getUser(2);
//if service failed available will be skipped
user.available(
user=>console.log("skipped:",user)
);
//both catch and notAvailable will be called
user.notAvailable(
arg=>console.log("not available:",arg)
);
user.notAvailable(
arg=>console.log("still not available:",arg)
);
//not handling catchError does not cause uncaught promise exception
// but you can inspect the error
// user.catchError(
// err=>console.log("error is::",err)
// );
var user = getUser(1);
//you can call available on user multiple times
user.available(
user=>console.log("got user:",user)
);
user.available(
user=>Promise.resolve(user)
.then(user=>console.log("still got user:",user))
.then(x=>Promise.reject("have to catch this one though"))
.catch(e=>console.log("ok, got error trying to run available block:",e))
);
//showing you can inspect the error
var user = getUser(5);
user.catchError(err=>console.log("error from service:",err));

Categories