javascript forEach appears to be nonblocking - javascript

Reading other posts here the consensus is the forEach should be synchronous and blocking.
However I must have done something strange in my code because it doesn't appear that way:
var noDupes = false; // should be true but force no inserts for now
console.log('forEach');
courses.forEach((course) =>
{
const promiseNoDupe = new Promise((resolve, reject) =>
{
dbo.collection("courses").findOne({ id: course.id }, (err, result) =>
{
if (err) throw err;
if (result) { console.log('dupe'); return reject('dupe'); }
console.log('nodupe');
resolve('nodupe');
});
});
noDupes &= promiseNoDupe.then(() =>
{
console.log('true promise');
return true;
}).catch(() =>
{
console.log('false promise');
return false;
});
});
console.log('End forEach');
if (noDupes)
{
console.log('Inserting many');
dbo.collection("courses").insertMany(courses, (err, result) =>
{
if (err) return res.status(400).send(error.details[0].message);
res.send(courses);
});
}
else
{
console.log('No Dupes allowed');
res.status(400).send('Inserting duplicate ID not Allowed!');
}
Console output:
forEach
End forEach
No Dupes allowed
nodupe
true promise
nodupe
true promise
The end forEach is executed before the promise is completed and before any of the internal processing is conducted! Subsequently the logic waiting on the promise is processing ahead of time.
I'm not sure what is going wrong but I'm trying to wait for the completion of all checks in the forEach before committing any new records.

Thanks to charlietfl for steering me towards map() and Promise.all().
Here is the working code:
var dupePromises = courses.map((course) =>
{
return new Promise((resolve, reject) =>
{
dbo.collection("courses").findOne({ id: course.id }, (err, result) =>
{
if (err) throw err;
if (result) return reject(false);
resolve(true);
});
}).then(() =>
{
return true;
}).catch(() =>
{
return false;
});
});
Promise.all(dupePromises).then((results) =>
{
if (results.every((isnotDupe) => { return isnotDupe /* == true */ }))
{
dbo.collection("courses").insertMany(courses, (err, result) =>
{
if (err) return res.status(400).send(error.details[0].message);
res.send(courses);
});
}
else{
res.status(400).send('Inserting duplicate ID not Allowed!');
}
});
}

Related

Understanding promises

Let's say I have this 'pseudo'code
static async fetchAuthData () {
AsyncStorage.getItem('authtoken', (err, value) => AuthData.token = value)
.then( (value) => {
{ ...Ok token fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
AsyncStorage.getItem('userid', (err, value) => AuthData.userid = parseInt(value))
.then( (value) => {
{ ...Ok userid fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
if (token ok && userid ok ) {
return Promise.resolve('ok');
}
else {
return Promise.reject('Some sort of error');
}
}
I assume if (token ok && userid ok ) will not get executed until the previous two promises are resolved or rejected.
Am I right?.
Is there some possibility for if (token ok && userid ok ) get executed before I even get the token?.
This static method is called at the very beginning of my app and this is where I decide to go directly to the app or navigate to the auth flow.
The documentation doesn't seem to be clear about this.
Add await to the AsyncStorage functions to pause the execution till promise gets resolved and goes to next line
static async fetchAuthData () {
await AsyncStorage.getItem('authtoken', (err, value) => AuthData.token = value)
.then( (value) => {
{ ...Ok token fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
await AsyncStorage.getItem('userid',(err, value) => AuthData.userid = parseInt(value))
.then( (value) => {
{ ...Ok userid fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
if (token ok && userid ok ) {
return Promise.resolve('ok');
}
else {
return Promise.reject('Some sort of error');
}
}
as #Amadan said, It will get executed before you get the token
, you need to wait all the promises until they're resolve
static async fetchAuthData () {
try {
const [authtoken, userid] = await Promise.all([
() => AsyncStorage.getItem('authtoken'),
() => AsyncStorage.getItem('userid'),
]
if (token ok && userid ok ) {
return Promise.resolve('ok');
}
} catch(err) {
// handle exception
}
}
You can make use of async and await which makes code to execute in a synchronous way.
Check the below code which does the same.
static async fetchAuthData () {
try{
AuthData.token = await AsyncStorage.getItem('authtoken')
userId = await AsyncStorage.getItem('userid')
AuthData.userId = parseInt(userId)
return true;
}catch(err){
console.log(err)
return false
}
}
If any errors comes up it will be catched in catch block and return false from here.
static async fetchAuthData() {
await new Promise((resolve, reject) => {
AsyncStorage.getItem('authtoken', (err, value) => AuthData.token = value)
.then((value) => {
resolve('...Ok token fetched...')
})
.catch((err) => {
reject('Some sort of error');
});
})
await new Promise((resolve, reject) => {
AsyncStorage.getItem('userid', (err, value) => AuthData.userid = parseInt(value))
.then((value) => {
resolve('...Ok token fetched...')
})
.catch((err) => {
reject('Some sort of error');
});
})
if (token ok && userid ok) {
return Promise.resolve('ok');
} else {
return Promise.reject('Some sort of error');
}
}

break from promise mapseries

I have an array of methods and I running them by Promise.mapSeries:
Promise.mapSeries(functions.map(function => {
return new Promise((resolve, reject) => {
function(req, res, function(err) {
if (success) {
return something(); //if we here, break mapSeries
}
resolve();
});
})
}))
.then(() => {
return something();
})
How I can break from mapSeries, to prevent execution rest of functions?
Since you are returning something() you won't execute resolve(); so maybe try to resolve the promise before?
Promise.mapSeries(functions.map(function => {
return new Promise((resolve, reject) => {
function(req, res, function(err) {
if (success) {
return Promise.resolve(something()); //if we here, break mapSeries
}
});
})
}))
.then(() => {
return something();
})

Promise do not bubble outside

I need to test function testMe using Mocha. But there is trouble when my unit test throw an error. Here is simpified example
function testMe(callback) {
new Promise((resolve, reject) => {
setTimeout(() => resolve([1,2,3]), 1000);
}).then((result) => {
callback(null, result);
}).catch((error) => {
callback(error, null)
});
}
testMe((err, result) => {
if(err) throw new Error();
if(result.length < 5) throw new Error();
});
In this example after throw runs catch block. But I need to run catch block only after reject.
EDIT:
In this case the script never stop. I don't understand why.
function testMe(callback) {
new Promise((resolve, reject) => {
setTimeout(() => resolve([1,2,3]), 1000);
}).then((result) => {
callback(null, result);
}, (error) => {
callback(error, null)
}).catch(() => {
console.log('Do not throw an error but still running');
});
}
testMe((err, result) => {
if(err) throw new Error();
if(result.length < 5) throw new Error();
});
When you work with promises then return the promises from functions instead of taking callbacks.
For example, instead of:
function testMe(callback) {
new Promise((resolve, reject) => {
// ...
});
}
use:
function testMe(callback) {
return new Promise((resolve, reject) => {
// ...
});
}
that way you will have the promise available to the caller of the function.
If you need to mix both styles, i.e. returning promises and taking callbacks, consider using a reliable library to handle that for you especially if you have trouble coding the translation between those style yourself:
http://bluebirdjs.com/docs/api/ascallback.html
http://bluebirdjs.com/docs/api/promise.promisify.html
You can simply return the promise from the test:
function testMe() {
// ^^ drop the callback
return new Promise((resolve, reject) => {
// ^^^^^^ return the promise
setTimeout(() => resolve([1,2,3]), 1000);
});
}
var p = testMe().then(result) => {
// ^^^^^ use the promise
if(result.length < 5) throw new Error();
});
return p; // to mocha

How to stop promise chain after resolve?

I want to stop promise chain after it resolved via some conditions. Below code is might useful to understand what am I saying.
function update(id, data) {
return new Promise((resolve, reject) => {
let conn;
pool.get()
.then((db) => {
conn = db;
if(Object.keys(data).length === 0) {
return resolve({ updated: 0 });
}
else {
return generateHash(data.password);
}
})
.then((hash) => {
conn.query("UPDATE ... ", (err, queryResult) => {
if(err) {
throw err;
}
resolve({ updated: queryResult.affectedRows });
});
})
.catch((err) => { ... })
});
}
Note that pool.get() is promise wrapped API for getting connection pool from MySQL module that I made.
What I'm trying to do is updating user data. And for save server resources, I avoided to update if no data to update(Object.keys(data).length === 0).
When I tried this code, second then(updating db) is always happening even if no data to update!
I read this post, but it didn't worked. Why the promise chain wasn't stopped when I called "return resolve();"? And how to I stop it properly? I really like using Promises, but sometimes, this kind of things make me crazy. It will be very appreciate to help me this problem. Thanks!
P.S. I'm using node v6.2.2 anyway.
Why the promise chain wasn't stopped when I called "return resolve();"?
You've returned from the current then callback and fulfilled the outer promise. But that doesn't "stop" anything, then then chain still will continue by resolving with the return value of the callback.
And how to I stop it properly?
You need to put the then call inside the if to have the condition apply to it:
pool.get()
.then((db) => {
…
if (Object.keys(data).length === 0) {
…({ updated: 0 });
} else {
return generateHash(data.password)
.then((hash) => {
conn.query("UPDATE ... ", (err, queryResult) => {
…
});
})
}
})
.catch((err) => { ... })
And in any case, you should avoid the Promise constructor antipattern! You should only promisify the query method:
function query(conn, cmd) {
return new Promise((resolve, reject) => {
conn.query(cmd, (err, queryResult) => {
if (err) reject(err); // Don't throw!
else resolve(queryResult);
});
});
}
and then use that:
function update(id, data) {
return pool.get()
.then(conn => {
if (Object.keys(data).length === 0) {
conn.close(); // ???
return { updated: 0 };
} else {
return generateHash(data.password)
.then(hash => {
return query(conn, "UPDATE ... ")
}).then(queryResult => {
conn.close(); // ???
return { updated: queryResult.affectedRows };
}, err => {
…
conn.close(); // ???
});
}
});
}
Notice that it might not make sense to get a connection from the pool if you can know beforehand that no query will be made, so probably you should put the if on the top level:
function update(id, data) {
if (Object.keys(data).length === 0) {
return Promise.resolve({ updated: 0 });
} else {
return pool.get()
.then(conn => {
return generateHash(data.password)
.then(hash => {
return query(conn, "UPDATE ... ")
}).then(queryResult => {
conn.close(); // ???
return { updated: queryResult.affectedRows };
}, err => {
…
conn.close(); // ???
});
});
}
}
This would be a good situation to use an if statement:
function update(id, data) {
if (Object.keys(data).length === 0) {
return Promise.resolve({ updated: 0 });
}
let conn;
return pool.get()
.then((db) => {
conn = db;
return generateHash(data.password);
})
.then((hash) => {
return new Promise(function (resolve, reject) {
conn.query("UPDATE ... ", (err, queryResult) => {
if(err) {
reject(err);
}
resolve({ updated: queryResult.affectedRows });
});
});
})
.catch((err) => { ... })
}

Using throw in promises

I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
function promiseFunc(options) {
return new Promise(() => {
return options;
});
}
function myfunc(options) {
return new Promise(() => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then((result) => {
if (result.throwerr) throw new Error("thrown on purpose");
return result.value;
});
});
};
My test as follows:
const myfunc = require("./myfunc");
describe('myfunc', () => {
it('should fail without options', () => {
return myfunc()
.then((result) => { throw new Error(result) }, (err) => {
console.log("test #1 result:", err.message === "missing options");
});
});
it('should fail on options.throwerr', () => {
return myfunc({throwerr: true})
.then((result) => {}, (err) => {
console.log("test #2 result:", err.message === "thrown on purpose");
});
});
it('should return options.value', () => {
return myfunc({value: "some result", throwerr: false})
.then((result) => {
console.log("test #3 result:", result === "some result");
}, (err) => {});
});
});
The first test pass, but the second and third fails.
Log #2 does not even run, so I assumed the "throw on purpose" messes up something, therefore I created test #3, where I don't throw anything, but it still fails.
What am I missing?
Solution:
function promiseFunc(options) {
return new Promise(resolve => {
return resolve(options);
});
}
function myfunc(options) {
return new Promise((resolve, reject) => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then(result => {
if (result.throwerr) throw new Error("thrown on purpose");
return resolve(result.value);
}).catch(err => {
return reject(err);
});
});
};
You forgot to pass a function with resolve and reject parameters, so your promises just don't work.
function promiseFunc(options) {
return new Promise(resolve => { // resolve function
resolve(options)
})
}
module.exports = function myfunc(options) {
return new Promise((resolve, reject) => { // since you may either resolve your promise or reject it, you need two params
if (!options) {
return reject(new Error("missing options"))
}
return promiseFunc(options).then(result => {
if (result.throwerr) {
return reject(new Error("thrown on purpose"))
}
resolve(result.value)
})
})
}
... and the test (mocha)
const assert = require('assert'),
myfunc = require("./myfunc")
describe('myfunc', () => {
it('should fail without options', done => { // mind the callback, promises are always async
myfunc()
.catch(err => {
assert(err.message === "missing options")
done() // <- called here
})
})
it('should fail on options.throwerr', done => {
myfunc({throwerr: true})
.catch(err => {
assert(err.message === "thrown on purpose")
done()
})
})
it('should return options.value', done => {
return myfunc({value: "some result", throwerr: false})
.then(result => {
assert(result === "some result")
done()
})
})
})
I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
This will do it ...
var q = require('q'); // In recent versions of node q is available by default and this line is not required
function iReturnAPromise(num) {
var def = q.defer();
if (typeof num=== 'number') {
try {
var value = 100 / num;
def.resolve(value);
} catch(e) {
def.reject("oops a division error - maybe you divided by zero");
}
} else {
def.reject("o no its not a number");
}
return def.promise;
}
PS this function was coded freehand and has not been tested - but this will work. Obviously try catch should be used sparingly.
PS I prefer the q library implementation of promise instead of the default node promise library - they take a very different approach. q dispenses with all the wrapping!
using the promise library u wanted ...
function iReturnAPromise(num) {
return new Promise(function(resolve, reject) {
if (typeof num === 'number') {
try {
var value = 100 / num;
resolve(value);
} catch (e) {
reject("oops a division error - maybe you divided by zero");
}
} else {
reject("o no its not a number");
}
})
}
iReturnAPromise(7).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
// Unexpectedly this is not an error in node 5.6 because div by 0 is not an error operation anymore!
iReturnAPromise(0).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
iReturnAPromise("fred").then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
you can see why i prefer the q syntax :)

Categories

Resources