Handling error when using [].reduce to call several functions sequentially - javascript

I have the following code to run 15 promises sequentially:
[func1, func2, func3, ...etc].reduce((prev, curr) =>
prev.then(curr).catch(console.error)
, Promise.resolve())
The problem is: When a error happens inside of one of the functions, the following error popups in my terminal:
Cannot read property 'reduce' of undefined
I've searched, but wasn't able to find someone with the same error. Is there a workaround?
Thank you.

My crystal ball says that inside of one of these functions you are using reduce on the array that you receive as an argument, as the result of the previous function.
The problem with that is when an error happens, your .catch(console.error) does handle it immediately and returns a promise that is fulfilled with undefined (the return value of console.error(…)). This result is then passed to the next function, which is executed regardless of the error. You'll want to handle the error in the end only:
[func1, func2, func3, …].reduce((prev, curr) =>
prev.then(curr)
, Promise.resolve()).catch(console.error)

Related

Can't store data from db.countDocuments in mongoDB

I'm pretty new to Javascript, and I don't understand why I can't save the result of this code below.
I'm working with MongoDB and I want to store the number of documents in a cluster so that I can keep track of how many are in the cluster, however, when I try to log the output on the third line, it is undefined. Why is this?
let theIndex;
collection.countDocuments({}).then(res => theIndex = res);
console.log(theIndex);
collection.countDocuments({}) returns a promise so you should use console.log once it resolves:
collection.countDocuments({}).then(res => {
theIndex = res;
console.log(theIndex);
});
The callback function inside .then is executed once the promise resolves. Logging it outside the callback won't work because that console.log line gets executed immediately and at a later point of time(because reaching the database and such takes time) the callback is called.
Note that even if the promise resolves immediately(as fast as possible), the callback still gets executed later because of the event loop in javascript.

How do you chain queries in order using knex.js?

I'm having some trouble understanding how the promises in Knex.js work (uses Bluebird.js for promises). I'm trying to do something pretty simple, execute different insert statements one after another in order, but I haven't been able to get it to work.
Here's the code I have so far, which is meant to execute an insert on the authentication_type table, then an insert on the user_table, and then an insert on the category table.
// Import database connection
var knex = require('./db-connection.js');
// Add a row to authentication_type table so that user's can be created
function add_authentication_type() {
return knex('authentication_type')
.insert({id: 1, name: 'Internal'})
}
// Add a 'default' user with nil uuid
// Anything added without a user must link back to this user
function add_default_user() {
return knex('user_table')
.insert({user_table_id: knex.raw('uuid_nil()'),
authentication_type: 1,
authentication_token: "default"})
}
// Add categories so that locations can be created
function add_categories() {
return knex('category')
.insert([
{name: "Hospital",
description: "Where people go to get healed"},
{name: "Police Dept",
description: "Where people go when there’s trouble"},
{name: "Fire Dept",
description: "Where all the fire trucks are"}])
}
// Run the functions in the necessary order to fit constraints
add_authentication_type()
.then(add_default_user()
.then(add_categories()))
I need these inserts to happen in the correct order, from top to bottom, so I don't violate the constraints of my database. That's what I was attemping to do with the last few lines by chaining calls in the .then() portion of each call. I thought this would make the first query happen, then the second, then the third, but that doesn't appear to be the case since I get constraint violation errors when running this code.
I've been reading through the Knex and Bluebird pages, but I just can't get a grasp on it. What is the proper way to go about executing this kind of sequential querying with Knex?
The knex query builder just returns a promise, so this is just a matter of correctly chaining those promises.
TL;DR: Do this:
add_authentication_type()
.then(add_default_user)
.then(add_categories)
Promise Chaining
The key to getting your code to work is understanding these four lines do different things:
// A
.then(add_default_user)
// B
.then(() => add_default_user())
// C
.then(add_default_user())
// D
.then(() => add_default_user)
then will call whatever function is passed as an argument to it after the preceding promise resolves. In A it calls add_default_user, which returns a promise. In B, it calls that entire function, which itself returns a promise-returning function. In both of these cases, then calls a function that eventually returns a promise, which is how you correctly chain promises.
C will not work as expected, because you're not passing a function to then, but the result of the function call. Because promises, like callbacks, are asynchronous, this returns undefined and also immediately calls that function, instead of waiting for the previous promise to resolve.
D won't work because the function you're passing in to then doesn't actually call add_default_user!
Flattening the Chain
If you're not careful, you can end up with functional, but not exactly readable code (a "promise hell" similar to callback hell).
foo()
.then((fooResult) => bar(fooResult)
.then((barResult)=> qux(barResult)
.then((quxResult)=> baz(quxResult)
)
)
)
This works, but is unnecessarily messy. If the function passed to then returns a promise, the first then call can be followed up with a second one. The value the promise inside the first then resolves to will then be passed to the function inside the second then. That means the above can be flattened to:
foo()
.then((fooResult) => bar(fooResult))
.then((barResult)=> qux(barResult))
.then((quxResult)=> baz(quxResult))
**PROTIP:**If you're anal about lining up your calls, you can also kick off your promise chain with a Promise.resolve() like this:
Promise.resolve()
.then(() => knex('table1').del())
.then(() => knex('table2').del())
.then(() => knex('table3').del())

Why Rx.Observable.throw(x) does not actually throw?

I am playing around with Rxjs, observables and maps, and I found a strange behavior for Observable.throw(error) that I cannot explain.
If I have a Rx stream that uses a map operator, and I want to interrupt the process, I would have expected the method Observable.throw to be appropriate, however, this does not seem to be the case.
Consider the following example:
Rx.Observable.just("test 1").subscribe(
x => console.log(x),
err => console.log(err),
() => console.log("done, no errors")
);
Now let's introduce an error, if I use the regular throw from Javascript it works as expected:
Rx.Observable.just("test 2").map(
x => { throw new Error(x) }
).subscribe(
x => console.log(x),
err => console.log(err),
() => console.log("done - error was thrown, as expected")
);
Output:
Error: test 2
at Rx.Observable.just.map.x ((index):58)
at c (rx.all.compat.js:61)
at e.onNext (rx.all.compat.js:5169)
(...)
But if I use Rx.Observable.throw(...), the error callback from the following subscriber is never called, and the next callback will be called instead with some strange object that seems to be an Rx error object.
Rx.Observable.just("test 3").map(
x => Rx.Observable.throw(x)
).subscribe(
x => console.log(x),
err => console.log(err),
() => console.log("done - it does not work... why?")
);
Output:
b_subscribe: f(a)error: "test 3" scheduler: a__proto__: g
As #Whymarrh pointed out, it seems to work fine if I use a flatMap operator instead.
Documentation for map:
The Map operator applies a function of your choosing to each item
emitted by the source Observable, and returns an Observable that emits
the results of these function applications.
Documentation for Observable.throw:
Returns an observable sequence that terminates with an exception,
using the specified scheduler to send out the single onError message.
Does anyone know why when using Observable.throw inside the map operator the error callback is not called, and why the process is not interrupted?
Example in jsfiddle
I know that I can just use the regular throw and move on, I already have a working solution, I am posting this question out of curiosity to have a better understanding of how the framework works.
Quick reminder: stackoverflow has a be nice policy.
One of the comments properly answers this question:
throw new Error(x) throws an exception that simply bubbles to the top of the process.
x => Rx.Observable.throw(x) just creates a structure representing an error. To the map operator, this structure is a value like any other, to call the success handler. flatMap on the other hand will take the structure and unwrapped it, then call the error handler.

How to test a method returning a Promise

I'm writing an Angular 2 RC5 application, and unit testing using Karma and Jasmine.
I have a method that returns a Promise<Foo> (It's on top of a call to angular's http.post) I want to run some assertions after that finishes.
Something like this doesn't work
let result = myService.getFoo();
result.then(rslt => expect(1+1).toBe(3)); // the error is lost
This creates an 'Unhandled Promise rejection' warning, but the error is suppressed and the test passes. How do I run assertions based on my resolved promise?
Notes:
The .catch() method doesn't seem to be what I'm after. I don't want to log or do anything that continues normal program flow, I want to fail the test.
I've seen code that looks like $rootScope.$digest();. I'm not sure what the typescript equivalent of this sort of thing is. There doesn't seem to be a way of saying: "I have a promise, I'm going to wait here until I have a synchronous result".
The test should look something like this:
it('should getFoo', function (done) {
let result = myService.getFoo();
result
.then(rslt => expect(rslt).toBe('foo'))
.then(done);
});
Using the done callback works, but you should be able to do this as well:
(Note the return)
it('should getFoo', function () {
let result = myService.getFoo();
return result
.then(rslt => expect(rslt).toBe('foo'))
});

When using promises why is the last then called?

I'm watching this video(code around 36 minutes) on Promises, but I'm confused by a bit of code:
getUser('mjackson')
.then(getTweets)
.then(updateStatus)
.then(undefined, handleError);
My problem with understand this is why is the last then called? And why does it pass undefined as the first argument?
If getTweets() fails, then updateStatus() is never called. Which is why I'm confused as to why the last then is called if the second (the one that contains updateStatus()) isn't.
I know handleError is a callback, I just don't get why undefined is passed.
I hope that makes sense.
Any then with a function provided as the second parameter will catch and handle any rejected promises farther up the promise chain.
So if getUser or getTweets or updateStatus fails, the error will be handled in handleError.
This code is passing undefined as the first parameter because in this case updateStatus is the last thing that the person writing it wants to do, and there's nothing more to do if that succeeds.
Most promise libraries, and the ES6 promise standard provide a .catch(...) method that is really just a shorthand for .then(undefined, ...):
getUser('mjackson')
.then(getTweets)
.then(updateStatus)
.catch(handleError);
There are two ways of using the promise methods, for async flow control, particularly for error-handling.
The first is:
anOperation.then(onsuccess, onerror);
The second is:
anOperation.catch(onerror);
If onsuccess is undefined or null in the first case, it means that there is no success option there (so it would move to the success after that one, in the success case).
Most promises you see/use are .then(onsuccess, undefined).
It should be noted:
If you throw an error in onsuccess, the onerror does not get fired. It moves to the next error down. It is impossible to fire both callbacks in the same .then, it's an either-or scenario
If you return a value from a .catch or an onerror callback, it is expected that you have solved the issue, and thus, you go into the next onsuccess of the next .then, with the value you returned. If this isn't what you want, rethrow, or return a Promise.reject
The signature of the then function is:
p.then(onFulfilled, onRejected);
If you pass undefined as the first parameter, it behaves as p.catch(onRejected);
In a then chain, every success functions will be called in an order unless one failed. If one failed, all the execution will break unless a second parameter of a then exists. The second parameter will behave the catch of any.
If a then function returns a promises, you can make a then chain.
handleError has called, because one of the previous promises in the chain was failed.
Promises in a chain with an example
Here my JsBin to understand a promise chain. Try to change giveMeSuccessPromise and giveMeFailPromise functions in doX or catchX functions.
In a chain of then clauses as below:
p.then(doFirst)
.then(doSecond)
.then(doThird)
.then(undefined, catchFirstSecondThird)
.then(doFourth)
.then(doFifth, catchEveryThing);
In this example, in case of any of the doFirst, doSecond or doThird failed catchFirstSecondThird will be executed. In case of any of the catchFirstSecondThird, doFourth or doFifth failed catchEveryThing will be executed.
The order of the execution in happy path:
doFirst, doSecond, doThird, doFourth, doFifth
The order of the execution when only doFirst fails:
doFirst, catchFirstSecondThird, doFourth, doFifth
The order of the execution when only doSecond fails:
doFirst, doSecond, catchFirstSecondThird, doFourth, doFifth
The order of the execution when both doFirst and doFourth fails:
doFirst, catchFirstSecondThird, doFourth, catchEveryThing
Resources:
From MDN, Promise API
From MDN, API of then
From MDN, API of catch
A very good tutorial about promises

Categories