Reputation: 2040
So in the first example, the chain executes all the way to the last console.log, without an error, but in the second example, which is similar, it does error out. Why does this happen and can I somehow make the first example also throw an error?
I only know it has to do with adding functions to an object, because if test
would be undefined, the chain would break, but I cannot understand how accessing a non-existant key in an object doesn't cause an error.
Edit: I just realized my brainfart. Naturally accessing test.something
returns undefined
instead of an error.
// EXAMPLE 1: Weird
var test = {};
test.f1 = function(result) {
return new Promise(function(resolve, reject) {
console.log("result", result);
resolve(1)
});
}
test.f2 = function(result) {
return new Promise(function(resolve, reject) {
console.log("result", result);
resolve(2)
});
}
test.f3 = function(result) {
return new Promise(function(resolve, reject) {
console.log("result", result);
resolve(3)
});
}
test.f4 = function(result) {
console.log("result", result);
}
test.f1(1)
.then(test.f2)
.then(test.f3)
.then(test.shouldCauseError)
.then(test.f4)
// EXAMPLE 2: As expected
new Promise(function(resolve, reject) {
resolve(1);
})
.then(function(result) {
return new Promise(function(resolve, reject) {
resolve(2)
});
})
.then(function(result) {
return new Promise(function(resolve, reject) {
resolve(3)
});
})
.then(shouldCauseError)
.then(function(result) {
console.log("result:", result);
});
Upvotes: 1
Views: 47
Reputation: 18619
In a .then
listener, both arguments are optional. That is, the following is completely valid:
Promise.resolve().then() //Nothing passed to then
Which is the same as if you write:
Promise.resolve().then(undefined, undefined)
As accessing objects' non-existent properties return undefined
, so...
.then(test.shouldCauseError)
...is essentially the same as...
.then(undefined)
...and it doesn't throw.
In the second case, however, it does throw, but it has nothing to do with .then
: Accessing a non-existent variable throws a ReferenceError
.
So the following also throws:
shouldCauseError
...before shouldCauseError
's value could even be passed to .then
Upvotes: 1
Reputation: 370679
.then
accepts up to two arguments. The first argument, if it exists, runs if the upper Promise resolved. The second argument, if it exists, runs if the upper Promise rejected.
Passing undefined
as the first argument (or as both arguments) to .then
is permitted - since no arguments exist, the upper Promise will be passed through the .then
unchanged.
(also, .then(undefined, fn)
is equivalent to .catch(fn)
)
So, your
.then(test.shouldCauseError)
passes undefined
to .then
, which means that the upper Promise is passed through the .then
without modification (and without throwing) - it goes to the next .then
in the chain, as if .then(test.shouldCauseError)
wasn't there.
Since passing undefined
to .then
is perfectly fine, you need some other method to create an error - perhaps wrap everything you pass to .then
s in a function that checks if the passed expression is actually a function, and if not, returns a function that throws:
.then(validate(test.shouldCauseError))
// EXAMPLE 1: Weird
var test = {};
test.f1 = function(result) {
return new Promise(function(resolve, reject) {
console.log("result", result);
resolve(1)
});
}
test.f2 = function(result) {
return new Promise(function(resolve, reject) {
console.log("result", result);
resolve(2)
});
}
test.f3 = function(result) {
return new Promise(function(resolve, reject) {
console.log("result", result);
resolve(3)
});
}
test.f4 = function(result) {
console.log("result", result);
}
const validate = arg => {
if (typeof arg !== 'function') {
return () => {
throw new Error();
}
}
return arg;
};
test.f1(1)
.then(validate(test.f2))
.then(validate(test.f3))
.then(validate(test.shouldCauseError))
.then(validate(test.f4))
.catch((err) => {
console.log('Error caught');
});
You could also make test
a Proxy that returns a throwing function when a property is accessed which doesn't exist on the target object.
But these are both weird solutions, because the "problem" being solved isn't considered a problem in JS. If you want to avoid these sorts of mistakes, I'd prefer to use something like Typescript to ensure that what I'm passing is of the right type, and not undefined
.
Upvotes: 2