How to chain Promises and callback-style code - javascript

I'm confused on how this chaining for promises work, I'm still fairly new to promises and js in general so excuse me
line three, return user.findOne({email}).then((user) => {, i'm just confused about how returning this promise does anything since it returns a other promise inside the .then()
UserSchema.statics.findByCredentials = function(email, password){
user = this;
return user.findOne({email}).then((user) => {
if (!user){
return Promise.reject();
}
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err, res) => {
if (res){
resolve(user);
}else{
reject()
}
});
});
});
}
the findByCredentials model method being used in an express app
app.post("/users/login", (req, res) => {
var body = _.pick(req.body, ["email", "password"]);
User.findByCredentials(body.email, body.password).then((user) => {
res.send(body)
}).catch((e) => {
res.send("!");
})
A simpler example I just created, this part
return plus(1).then((res) => {
return new Promise((resolve, reject) => {
is the problem i'm having trouble understanding
function plus(a) {
return new Promise((resolve, reject) => {
resolve(a + 1);
});
}
function test() {
return plus(1).then((res) => {
console.log(res);
return new Promise((resolve, reject) => {
resolve("Test");
});
});
}
test().then((res) => {
console.log(res);
});

As #Bergi said in the comment of your OP, the true power or Promises comes from returning them in the then of other Promises.
This allows you to chain Promises in a clean way.
To chain Promises all your operations in the chain must be Promises.
Your bcrypt.compare function uses callbacks to signal that it's done, so you need that function convert to a Promise.
This is easy to do. Just wrap the callback-style code in a Promise and resolve the result of the callback or reject if the callback is called with an err.
const comparePassword = (a, b) => {
return new Promise((resolve, reject) => {
bcrypt.compare(a, b, (err, result) => {
// Reject if there was an error
// - rejection is `return`-ed solely for stopping further execution
// of this callback. No other reason for it.
if (err) return reject(err)
// Resolve if not.
resolve(result)
})
})
}
... and then we can chain properly:
UserSchema.statics.findByCredentials = function(email, password) {
// Outer Promise:
// - Will eventually resolve with whatever the result it's inner
// promise resolves with.
return user.findOne({ email })
.then((user) => {
// Inner Promise:
// - Will eventually resolve with `user` (which is already
// available here), given that the password was correct,
// or
// reject with the bcrypt.compare `err` if the password was
// incorrect.
return comparePassword(password, user.password)
.then((result) => {
// This `then` belongs to the comparePassword Promise.
// - We use this so we can make sure we return the `user` we picked up
// from the previous `user.findOne` Promise.
// - This ensures that when you chain a `then` to this Promise chain
// you always get the `user` and not the result of `comparePassword`
return user
})
})
}
The key here is that whatever you return within a .then() is going to be passed as an argument to the next chained .then().
Additional info:
bcrypt.compare already returns a Promise, so we could have avoided the whole hassle of wrapping it into a Promise. I've intentionally used it with callbacks to illustrate how you should handle callback-style code in a Promise chain.

Related

Struggling with Promise.race() implementation

I saw following implementation of Promise.race().
I am finding it difficult of understand, how it's working.
const race = function(promisesArray) {
return new Promise((resolve, reject) => {
promisesArray.forEach((innerPromise) => {
Promise.resolve(innerPromise)
.then(resolve, reject)
.catch(reject);
});
});
};
1st part: Is the below statement true?
Promise.resolve(innerPromise) will always return a resolved promise with innerPromise as value and as it always resolves, I will always end up in .then block.
2nd Part:
I read in the explanation that resolve and reject passed to .then block will be called on resolution of innerPromise. Why?, shouldn't it be as Promise.resolve(innerPromise) always resolve, always 1st callback of .then block should get called?
I think I am missing something very basic. I have tried to find the solution but not able to find an explanation that clears my doubts.
The purpose of Promise.resolve in that code is to allow elements of the array to not be promises. They could be arbitrary thenables, or they could just be regular values (which will indeed become fulfilled promises).
const existingValue = 5;
const existingPromise = Promise.reject(new Error("blah"));
const existingThenable = {
then() {
console.log("never going to resolve >:)");
}
};
Promise.race([existingValue, existingPromise, existingThenable]).then(
value => { console.log("got a value", value); },
error => { console.log("got an error", error); },
);
1st part: Is the below statement true?
Promise.resolve(innerPromise) will always return a resolved promise with innerPromise as value and as it always resolves, I will always end up in .then block.
If you try removing existingValue from the array passed to Promise.race above, you’ll see that Promise.resolve doesn’t necessarily return a resolved promise; specifically, when it’s passed a promise or other thenable, it returns a promise that settles the same way (although when passed the same type of promise, it actually satisfies that obligation by returning the same promise object). So no, it’s not true. I think that answers part 2, too.
Additionally, although you didn’t bring it up: I’m pretty sure the additional .catch(reject) is unreachable/useless, at least for the standard ES Promise implementation.
Promise.resolve(anotherPromise) will always assume the state of anotherPromise, so if it anotherPromise is rejected, then the one from Promise.resolve() will also be rejected. Or if anotherPromise is fulfilled, the Promise.resolve() one will also be fulfilled with its value.
const rejectedPromise = Promise.reject("boom");
Promise.resolve(rejectedPromise)
.then(result => console.log("success:", result))
.catch(error => console.log("failure:", error));
See the documentation on MDN for Promise.resolve().
As for why the code is using Promise.resolve() instead of directly
innerPromise
.then(resolve, reject)
.catch(reject);
Promise.resolve() is useful when the input is not necessarily a promise. It can convert a plain value to a promise or an arbitrary thenable (potentially another promise implementation) to a vanilla JavaScript promise thus allowing for uniform way of handling the result.
Perhaps this is defensive coding or just allows for calling race(asyncResult, 42). The intention is not clear.
The resolve or reject parameters of into the executor function are noop when repeatedly called. A promise can reach a single final state - calling resolve/reject after that has no effect. Thus from the whole array, the first promise which leaves the pending state will determine what the promise constructor will be resolved as.
const p1 = new Promise((resolve, reject) => {
Promise.resolve("p1 success")
.then(resolve, reject)
.catch(reject);
Promise.reject("p1 failure")
.then(resolve, reject)
.catch(reject);
});
const p2 = new Promise((resolve, reject) => {
Promise.reject("p2 failure")
.then(resolve, reject)
.catch(reject);
Promise.resolve("p2 success")
.then(resolve, reject)
.catch(reject);
});
const p3 = new Promise((resolve, reject) => {
Promise.resolve("p3 hello")
.then(resolve, reject)
.catch(reject);
Promise.resolve("p3 world")
.then(resolve, reject)
.catch(reject);
});
const p4 = new Promise((resolve, reject) => {
Promise.reject("p4 foo")
.then(resolve, reject)
.catch(reject);
Promise.reject("p4 bar")
.then(resolve, reject)
.catch(reject);
});
p1.then(console.log, console.error);
p2.then(console.log, console.error);
p3.then(console.log, console.error);
p4.then(console.log, console.error);
Therefore, by looping and attaching the same resolve and reject to all promises, race will only resolve with the same outcome of the first promise to resolve. This matches the JavaScript implementation of Promise.race():
Return value
A Promise that asynchronously settles with the eventual state of the first promise in the iterable to settle. In other words, it fulfills if the first promise to settle is fulfilled, and rejects if the first promise to settle is rejected. The returned promise remains pending forever if the iterable passed is empty. If the iterable passed is non-empty but contains no pending promises, the returned promise is still asynchronously (instead of synchronously) settled.
N.B. iterable is the input to Promise.race(). It matches promisesArray of race().
With all that said, the following construct seems entirely superfluous:
p
.then(resolve, reject)
.catch(reject);
The second parameter to .then() is the onRejected callback. So if p is rejected, the second argument to .then() would be used to handle that. The extra .catch() would handle errors coming from either resolve or reject in the .then()
Promise.resolve("foo")
.then(
result => { throw `Fulfilled with ${result}. Throwing after success.` },
error => {throw `Fulfilled with ${error}. Throwing after error` }
)
.catch(errorFromThen => console.log(`Error in .catch() is: ${errorFromThen}`));
Promise.reject("bar")
.then(
result => { throw `Fulfilled with ${result}. Throwing after success.` },
error => {throw `Fulfilled with ${error}. Throwing after error` }
)
.catch(errorFromThen => console.log(`Error in .catch() is: ${errorFromThen}`));
Yet, neither resolve nor reject from the executor function can throw/reject in a plain Promise constructor.
//more verbose onError and .catch() handlers in order to showcase what gts shown or not
const p = new Promise((resolve, reject) => {
Promise.reject("hello")
.then(
resolve,
error => {
console.log("inside the onReject in .then()", error);
return reject(error);
})
.catch(error => {
console.log("inside the .catch()", error);
return reject(error);
});
Promise.reject("world")
.then(
resolve,
error => {
console.log("inside the onReject in .then()", error);
return reject(error);
}
)
.catch(error => {
console.log("inside the .catch()", error);
return reject(error);
});
});
p.then(console.log, console.error);
The extra .catch() is thus not used.
Overall, race() behaves like the vanilla JavaScript Promise.race(). The only major difference is that Promise.race() accepts any iterable, while race() only handles arrays.
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
const array = [delay(300, "a"), delay(100, "b"), delay(200, "c")];
const iterable = array.values();
Promise.race(iterable)
.then(result => console.log(`First to fulfil was ${result}`));
const race = function(promisesArray) {
return new Promise((resolve, reject) => {
promisesArray.forEach((innerPromise) => {
Promise.resolve(innerPromise)
.then(resolve, reject)
.catch(reject);
});
});
};
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
const array = [delay(300, "a"), delay(100, "b"), delay(200, "c")];
const iterable = array.values();
race(iterable)
.then(result => console.log(`First to fulfil was ${result}`))
.catch(console.error);

Promise chain continues after rejection

I'm having trouble to properly catch an error/reject in a promise chain.
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
}).then(p2).catch(() => {
console.log("Caught p2");
}).then(p3).catch(() => {
console.log("Caught p3");
}).then(() => {
console.log("Final then");
});
When the promise is rejected, the following .then still gets executed. In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
P1
P2
Caught p2
P3
Final then
The rejection gets caught correctly, but why is "P3" logged after the catch?
What am I doing wrong?
To clarify #evolutionxbox, this is my expected result:
Promise.resolve().then(() => {
console.log("resolve #1");
return Promise.reject();
}).then(() => {
console.log("resolve #2");
return Promise.resolve();
}).then(() => {
console.log("resolve #3");
return Promise.resolve();
}).then(() => {
console.log("Final end");
}).catch(() => {
console.log("Caught");
});
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
Here is a synchronous equivalent of your code:
const f1 = () => {
console.log("F1");
};
const f2 = () => {
console.log("F2");
throw new Error();
};
const f3 = () => {
console.log("F3");
};
try {
f1();
} catch {
console.log("Caught f1");
}
try {
f2();
} catch {
console.log("Caught f2");
}
try {
f3();
} catch {
console.log("Caught f3");
}
console.log("Final code");
As you can see, that gives a matching result. Hopefully, looking at the synchronous code you would not be surprised why. In a try..catch you are allowed to attempt recovery. The idea is that the catch will stop the error propagation and you can hopefully continue further. Or if you do want to stop, you still have to explicitly throw again, for example:
doCode();
try {
makeCoffee();
} catch(err) {
if (err instanceof IAmATeapotError) {
//attempt recovery
makeTea();
} else {
//unrecoverable - log and re-throw
console.error("Fatal coffee related issue encountered", err);
throw err;
}
}
doCode();
This is also the purpose Promise#catch() serves - so you can attempt recovery or at least act when there was a problem. The idea is that after the .catch() you might be able to continue:
const orderPizza = (topping) =>
new Promise((resolve, reject) => {
if (topping === "pepperoni")
reject(new Error("No pepperoni available"));
else
resolve(`${topping} pizza`);
});
const makeToast = () => "toast";
const eat = food => console.log(`eating some ${food}`);
async function main() {
await orderPizza("cheese")
.catch(makeToast)
.then(eat);
console.log("-----");
await orderPizza("pepperoni")
.catch(makeToast)
.then(eat);
}
main();
In order to reject the promise chain from a .catch() you need to do something similar as a normal catch and fail at the error recovery by inducing another error. You can throw or return a rejected promise to that effect.
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
The second piece of code you show fails entirely after a reject because there are no other .catch()-es that are successful. It is basically similar to this synchronous code:
try {
console.log("log #1");
throw new Error();
console.log("log #2");
console.log("log #3");
console.log("Final end");
} catch {
console.log("Caught");
}
Thus if you do not want to recover early, you can also skip the .catch() instead of inducing another error.
Try this.
const p1 = (arg) => {
// Promise returns data in the respected arguments
return new Promise((resolve, reject) => {
// Data to be accessed through first argument.
resolve(arg);
});
};
const p2 = (arg) => {
return new Promise((resolve, reject) => {
// Data to be accessed through second argument.
reject(arg);
});
}
p1('p1').then(resolve => {
console.log(resolve + ' is handled with the resolve argument. So it is accessed with .then()');
}) // Since reject isn't configured to pass any data we don't use .catch()
p2('p2').catch(reject => {
console.log(reject + ' is handled with the reject argument. So it is accessed with .catch()');
}) // Since resolve ins't configured to pass any data we don't use .then()
// You would normally configure a Promise to return a value on with resolve, and access it with .then() when it completes a task successfully.
// .catch() would then be chained on to the end of .then() to handle errors when a task cannot be completed.
// Here is an example.
const p3 = () => {
return new Promise((resolve, reject) => {
var condition = true;
if (condition === true) {
resolve('P3');
} else {
reject('Promise failed!');
}
});
};
p3('p3').then(resolve => {
console.log(resolve);
}).catch(reject => {
console.log(reject);
})
You do not do anything wrong.
In your code you call the first promise p1. Then you write p1.catch(...).then(...).then(...).then(...). This is a chain which means that you should call then 3 times, because you called resolve method in the p1 promise (all these thens depend on the first promise).
When the promise is rejected, the following .then still gets executed.
Yes. Just to be accurate: the then and catch method calls are all executed synchronously (in one go), and so all promises involved are created in one go. It's the callbacks passed to these methods that execute asynchronously, as the relevant promises resolve (fullfill or reject).
In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
This is not the case. The promise that a catch returns can either fullfill or reject depending on what happens in the callback passed to it, and so the callbacks further down the chain will execute accordingly when that promise resolves.
The rejection gets caught correctly, but why is "P3" logged after the catch?
As in your case the catch callback returns undefined (it only performs a console.log), its promise fullfulls! By consequence, the chained then callback -- on that promise -- is executed... etc.
If you want to "stop"
If you want to keep the chain as it is, but wish to have a behaviour where a rejection leads to no further execution of then or catch callbacks, then don't resolve the associated promise:
const stop = new Promise(resolve => null);
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
return stop; // don't resolve
}).then(p2).catch(() => {
console.log("Caught p2");
return stop;
}).then(p3).catch(() => {
console.log("Caught p3");
return stop;
}).then(() => {
console.log("Final then");
});

Promise.all is never triggered due Promise rejection

I have two promises. One that reads a sample.txt file and another that reads all the files from a /books/ folder. The second promise uses a function called readFiles, which takes the dirnames and uses them to look though each file. When all the promises are ready the code inside then should run:
const p1 = new Promise((resolve, reject) => {
fs.readdir(__dirname + '/books/', (err, archives) => {
// archives = [ 'archive1.txt', 'archive2.txt']
readFiles(archives, result => {
if (archives.length === result.length) resolve(result)
else reject(result)
})
})
})
const p2 = new Promise((resolve, reject) => {
fs.readFile('sample.txt', 'utf-8', (err, sample) => {
resolve(sample)
})
})
Promise.all([p1, p2]).then(values => {
console.log('v:', values)
}).catch(reason => {
console.log('reason:', reason)
})
function readFiles (archives, callback) {
const result = []
archives.forEach(archive => {
fs.readFile(__dirname + '/books/' + archive, 'utf-8', (err, data) => {
result.push(data)
callback(result)
})
})
}
However, Promise.all always get rejected:
reason: [ 'archive 1\n' ]
What am I doing wrong?
Promises are one-shot devices. Once they've been rejected or resolved, their state can never change. With that in mind, readFiles() calls its callback for every file that it reads and you reject or resolve every time that callback is called, but the way you are using it, you check:
if (archives.length === result.length)
which will never be true on the first one and then you reject. Once that promise is rejected, its state cannot change. Subsequent calls to the callback will also call reject() and then the last one will call resolve(), but the state is long since set so only the first call to reject() or resolve() actually does anything. The others are simply ignored. So, p1 will always reject, thus Promise.all() that uses p1 will always reject.
You need to change readFiles() to either only call its callback once when it is done with all the files or change it to return a single promise that resolves when all the files are read or change how you're using the callback so you don't reject the first time it is called.
In general, if you're going to use promises, then you want to promisify at the lowest level and use the advantages of promises (particular for error propagation) everywhere rather than mix callbacks and promises. To that end, I'd suggest:
fs.readFileP = function(fname, encoding) {
return new Promise(function(resolve, reject) {
fs.readFile(fname, encoding, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
function readFiles(archives, encoding, callback) {
return Promise.all(archives.map(function(file) {
return fs.readFileP(file, encoding);
}));
}
Or, going a level deeper and promisifying fs.readdir() also, you'd get this:
// helper functions
fs.readdirP = function(dir) {
return new Promise(function(resolve, reject) {
fs.readdir(dir, function(err, files) {
if (err) return reject(err);
resolve(files);
});
});
}
fs.readFileP = function(fname, encoding) {
return new Promise(function(resolve, reject) {
fs.readFile(fname, encoding, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
function readFiles(archives, encoding) {
encoding = encoding || 'utf8';
return Promise.all(archives.map(function(file) {
return fs.readFileP(file, encoding);
}));
}
// actual logic for your operation
const p1 = fs.readdirP(__dirname + '/books/').then(readFiles);
const p2 = fs.readFileP('sample.txt', 'utf-8');
Promise.all([p1, p2]).then(values => {
console.log('v:', values);
}).catch(reason => {
console.log('reason:', reason);
});
If you use the Bluebird promise library which makes it easy to promisify whole modules at once and has some extra functions for managing Promise flow control, then the above code simplifies to this:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
const p1 = fs.readdirAsync(__dirname + '/books/').then(files => {
return Promise.map(archives, file => {
return fs.readFileAsync(file, 'utf8');
});
});
const p2 = fs.readFileAsync('sample.txt', 'utf-8');
Promise.all([p1, p2]).then(values => {
console.log('v:', values);
}).catch(reason => {
console.log('reason:', reason);
});
In this block of code, the Promise.promisifyAll() line of code creates promisified versions of every method on the fs module with the Async suffix on them. Here, we use fs.readFileAsync() and fs.readdirAsync() so we can use promises for everything.

Promises not going in order of the "then()"s? (nodejs)

Why does the then() where it says console.log('THIS COMES BEFORE THE WRITING AND EXTRACTING'); statement come before the writing and extracting to PDFs? I'm new to Promises, so I am a bit confused by this. I want that then() statement to happen AFTER the writing and parsing happens.
function writeToPDF(data, fileName) {
return new Promise((resolve, reject) => {
data.pipe(fs.createWriteStream(fileName), err => {
reject();
});
data.on('end', () => {
resolve();
})
});
}
​
function extractPDF(pdf) {
return new Promise((resolve, reject) => {
extract(pdf, {splitPages: false}, (err, text) => {
if (err) {
console.log(err);
} else {
resolve(text);
}
});
});
}
​
request(link).then((success, failure) => {
let fileName = './name-' + Date.now() + '.pdf';
writeToPDF(data, fileName).then(() => {
extractPDF(fileName).then((text) => {
arrayOfDocuments.push(text);
})
}, () => {
//handle error
});
}).then(() => {
console.log('THIS COMES BEFORE THE WRITING AND EXTRACTING');
});
You are nesting your then values and actually have two different then routes — for lack of a better term.
So far as the javascript is concerned, once the first then is resolved in your request method, it can move on to the next then. Here, once writeToPdf is resolved, that part of the promise chain then moves on to the console.log statement since the previous promise at that level has been resolved. That make sense?
The request(link).then(...) block comprises an outer promise chain and two levels of nested promise chain.
If you write a fat-arrow function with a {block}, returns are not explicit, therefore :
neither of the promises derived from writeToPDF() or extractPDF() is returned,
the outer promise chain is not informed of the existence of inner promises or their settlement,
console.log('THIS COMES BEFORE ...') relies solely on the fulfillment of the promise returned by request(link), and is guaranteed to occur after PDF stuff commences but before it completes.
request(link).then((success, failure) => {
let fileName = './name-' + Date.now() + '.pdf';
return writeToPDF(data, fileName).then(() => {
//^^^^^^
return extractPDF(fileName).then((text) => {
// ^^^^^^
arrayOfDocuments.push(text);
})
}, () => {
//handle error
});
}).then(() => {
console.log('THIS COMES BEFORE THE WRITING AND EXTRACTING');
});

Promise pattern - using Promise.all()

I have a chain of functions for grabbing some JSON data, then inserting the data into a database. I want to wait for all of the inserts to complete, so I am trying to use Promise.all(). I know that Promise.all() needs an array (or iterable) of promises.
Here is my chain:
fetchBody().then(parseBody).then(prepareInserts).then(insertAll).then(function() {
console.log('done')
}); // Error handling and more stuff here
My code is hanging on the prepareInserts function:
// Returns an array of promises, which will be iterated over in Promise.all();
const prepareInserts = function (data) {
return new Promise(function (resolve, reject) {
const promises = data.map(function (d) {
return new Promise(function (resolve, reject) {
connection.query(queryString, [d.a, d.b, d.c, d.d], function (error) {
if (error) {
reject(error);
return;
}
resolve();
});
});
});
resolve(promises);
});
};
I think I have a fundamental misunderstanding of how I should be laying out the prepareInserts function; the queries are being executed there, which is not what I want. I want them to be inserted in the last function in the chain:
const insertAll = function (promises) {
return Promise.all(promises);
};
I think this is what you want:
const doInserts = data => {
return Promise.all(data.map(d =>
new Promise((resolve, reject) => {
connection.query(queryString, [d.a, d.b, d.c, d.d], error => {
if (error) {
reject(error);
return;
}
resolve(/* to what?? */);
});
}));
});
};
fetchBody().then(parseBody).then(doInserts).then(function() {
console.log('done')
});
You return a Promise.all() promise that resolves when all internal promises are resolved (with no value?). Each internal promise is created by mapping a data item (from data) to a promise, which is resolved or rejected depending on the query result.
If you could promisify connection.query outside of this code, it would make for a cleaner result, where you map to "promisifiedQuery" directly.

Categories

Resources