Is this code
Promise.all([
promise1,
promise2,
])
.then(() => doSomething())
equivalent to
promise1
.then(() => promise2)
.then(() => doSomething())
I thought they were equivalent, but they do not behave the same on an fortunejs and mocha application. Below are more details about this application
I'm using fortune.js and I would like to write some tests using mocha. What I'm trying to achieve is to use the beforeEach hook to truncate tables in the database and then insert some predifined values. So if I have two tables called customer and user I will do something like this
beforeEach(function () {
return Promise.all([store.delete('user'), store.delete('customer')])
.then(() => store.create('customer', {
id: '0987654321234567890',
// More data
}))
.then(() => store.create('user', {
id: 'qwertyuioppoiuytrewq',
customer: '0987654321234567890',
// More data
}));
});
This code is not stable and works sometimes and sometimes not without I can find why (around 50% success rate)
But if I switch to this code it is working :
beforeEach(function () {
return store.delete('customer')
.then(() => store.delete('user'))
.then(() => store.create('customer', {
id: '0987654321234567890',
// More data
}))
.then(() => store.create('user', {
id: 'qwertyuioppoiuytrewq',
customer: '0987654321234567890',
// More data
}));
});
I thought that
Promise.all([
promise1,
promise2,
])
.then(() => doSomething())
was equivalent to
promise1
.then(() => promise2)
.then(() => doSomething())
Since store.delete returns a Promise, why do I have a different behavior ?
This
Promise.all([
promise1,
promise2,
])
.then(() => doSomething())
starts to execute both promises at the same time and calls then() when the latest finished, while this
promise1
.then(() => promise2)
.then(() => doSomething())
starts with the first promise and when that's finished executes the second, and so forth.
No they are not equivalent.
The docs for Promise.all() state that, though the return value is the order of the input promises, they are not resolved in that order.
Chaining promises with .then() resolves each promise in order.
Related
I am using Mocha to test data base interactions in my code (so I cannot mock the database).
For the tests to work I want to clean up my database before each test. According to my research I should be using Mocha's ability to handle promises being returned by the before function.
Here is how I am trying to achieve this:
const Orm = require('../db');
describe('Loading Xml Files to Database should work', () => {
before(() => {
return Orm.models.File.destroy({force: true, truncate: true, cascade: true});
});
it('run test', () => {
loadXmlFileToDatabase(....); // this loads data from a
// file into the table "files"
Orm.Orm.query("SELECT * FROM files")
.then(function (result){
console.log(result);
})
.catch(function(error){
console.log(error);
});
});
});
I am, however getting zero rows back from my Query at the end of the code. If I omit the before() function everything wrks, so my conclusion is, that for some reason Mocha is not waiting for it to complete.
How do I make sure, the before() function completes, before my test is run?
Mocha expects a promise to be returned by a function for it to wait.
Either explicitly return a promise from plain functions or use async functions that await
describe('Loading Xml Files to Database should work', function(){
before(async function(){
await Orm.models.File.destroy({force: true, truncate: true, cascade: true});
});
it('run test', async function(){
await loadXmlFileToDatabase(....); // this loads data from a
// file into the table "files"
const result = await Orm.Orm.query("SELECT * FROM files");
expect(result).to.eql({})
})
});
I want to test create user, so after connection to the DB I want to delete all the users that I tested and after it, I want to create new for the test.(Mocha)
test_helper.js
mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.connection
.once('open', () => {
console.log("connected")
})
.on('error', (error) => {
console.warn('Warning', error)
});
beforeEach((done) => {
mongoose.connection.collections.users.drop(() => {
done();
}
)
})
create_test.js
describe('Creating', () => {
it('saves a user', () => {
const testUser = new User({ name: 'Test' });
testUser.save();
});
});
I am getting the next error
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
what do I miss?
Some topics before my answer:
Asynchronous coding:
If you don't know/ are sure how it works, I recommend you to stop and learn about it: callback,Promises, async / await.
Testing:
Basically the flow is : create some scenario and assert some case, for example the attached code. I created a user and tested if it really work.
Testing Asynchronous code: after you read about callback function you can understand that done() is a callback function that permit finish the current async. function and pass to the next async. function.
testUser.save() returns a Promise and you aren't handling it.
...
testUser.save().then(()=>{
assert(testUser.isNew === false)
done();
}
...
It should work, but if you want to test some scenarios one after other you should handle it.
describe('Creating', () => {
it('some test', (done) => {
// some logic
done()
}
it('another test', (done) => {
// some logic
done()
}
});
});
I've tried to call 2 queries toward postgres with nodeJS in a sequential way. I came to this code.
However I'm not really happy with it, it seems overcomplicated to do a simple thing. Is there a simpler way to do what I'm trying to do ?
Basically, I try to print "before 1st" on console then execute the select now(), then print the results, then write "after 1st", then print "before 2nd" then execute select now(), then print the results, then write "after 2nd"
getConn().
then(async() => {
console.log("before 1st selectNow2")
await selectNow2()
console.log("after 1st selectNow2")
})
.then(async() => {
console.log("before 2nd selectNow2")
await selectNow2()
console.log("after 2nd selectNow2")
})
async function selectNow2() {
await client.query('SELECT NOW()')
.then(res => {
console.log(res.rows[0])
})
.catch(err => {
console.log(err.stack)
})
}
You do not need to use await and then() in the same statement inside selectNow2(). Since you are using await, you will not be returning a promise which means the returned value will not have the method then().
performQuery().then(() => {
console.log("DONE");
});
async function performQuery(){
await getConn();
console.log("before 1st selectNow2");
await selectNow2();
console.log("after 1st selectNow2");
console.log("before 2nd selectNow2");
await selectNow2();
console.log("after 2nd selectNow2");
}
async function selectNow2) {
try{
await client.query('SELECT NOW()');
console.log(res.rows[0])
}catch(err){
console.log(err.stack)
}
}
I am making a test app using systeminformation. I'm trying to make it so that each then waits for the previous function to finish. The problem I'm having is that the functions I am running inside are also promises, so the next then runs before the function finishes.
const si = require('systeminformation');
var cpuObj;
function initCPU() {
return new Promise(resolve => {
si.cpu()
.then(data => cpuObj = data)
.catch(err => console.log(err))
.then(() => {
setTimeout(() => console.log("timer"), 3000);
})
.then(() => {
si.cpuTemperature().then(data => console.log(data));
})
.then(() => {
console.log("here");
});
});
}
function test() {
console.log(cpuObj);
}
initCPU().then(() => {
test();
});
Output:
here
{ main: -1, cores: [], max: -1 }
timer
Expected Output:
{ main: -1, cores: [], max: -1 }
timer
here
A few points that need to be addressed:
setTimeout() does not return a promise, so you need to promisify and return it.
Flatten your chain by returning the promises from within each of the continuations rather than attempting to chain continuations within other continuations (i.e. then() inside of then()).
Do not wrap the continuation chain with a promise constructor, as the chain itself is already a promise, just return it directly instead. This is considered an antipattern.
Do not use globals, because it makes the initCPU() no longer re-entrant safe. Multiple calls to initCPU() before the promise returned by the first call resolves will result in unexpected behavior otherwise. Instead, use the appropriate scope to pass values along, which in this case is the function itself.
Allow errors to propagate to the caller and let the caller decide how to handle the error. Do not handle errors from within initCPU() unless you expect to use a fallback and continue to provide meaningful data to the caller.
const si = require('systeminformation');
const delay = ms => new Promise(resolve => { setTimeout(resolve, ms); });
function initCPU() {
// use local scope, not global
let cpuObj;
// return this promise chain directly
return si.cpu()
.then(data => {
cpuObj = data;
// return the promise to the chain
return delay(3000);
})
// let caller handle errors
// .catch(err => console.log(err))
// flatten your chain
.then(() => {
console.log('timer');
// return the promise to the chain
return si.cpuTemperature();
})
// flatten your chain
.then(data => {
console.log(data);
console.log('here');
// pass data to caller
return cpuObj;
});
}
function test(cpuObj) {
// received from last continuation of initCPU()
console.log(cpuObj);
}
initCPU()
.then(test)
// handle error from caller
.catch(err => {
console.log(err);
});
If you just want to query the cpu object immediately, and query cpuTemperature after 3 seconds, I'd do something like this using Promise.all():
// default to 3 seconds, allow it to be configurable
function initCPU(ms = 3000) {
return Promise.all([
si.cpu(),
delay(ms).then(() => si.cpuTemperature())
]).then(([cpu, cpuTemperature]) => ({
cpu,
cpuTemperature
}));
}
function test (obj) {
console.log(obj.cpu);
console.log(obj.cpuTemperature);
}
initCPU()
.then(test)
.catch(err => {
console.log(err);
});
I am studying through2 and sequelize.
My codes:
return Doc.createReadStream({
where: { /*...*/ },
include: [
{
/*...*/
},
],
})
.pipe(through({ objectMode: true }, (doc, enc, cb) => {
Comment.findOne(null, { where: { onId: doc.id } }).then((com) => { /* sequelize: findOne*/
com.destroy(); /* sequelize instance destroy: http://docs.sequelizejs.com/manual/tutorial/instances.html#destroying-deleting-persistent-instances */
cb();
});
}))
.on('finish', () => {
console.log('FINISHED');
})
.on('error', err => console.log('ERR', err));
I am trying to express my question clearly. Doc and Comment are sequelize Models. I want to use stream to read Doc instances from database one by one and delete comments on each Doc instance. Comment.findOne and com.destroy() will both return promises. I want to the promises resolved for each doc and then call cb(). But my above codes cannot work, before com be destroyed, the codes already finish running.
How to fix it? Thanks
I wrap the above piece of codes in mocha test, like
it('should be found by readstream', function _testStream(){
/* wrap the first piece of codes here*/
});
But before stream finished reading, the test exist.
You can wait for another promise by returning the promise and using another .then.
You may need to check for the com result being null as well, before running .destroy().
.pipe(through({ objectMode: true }, (doc, enc, cb) => {
Comment.findOne(null, { where: { onId: doc.id } })
.then(com => com.destroy())
.then(()=> cb())
.catch(cb)
}))
Then when running the test in mocha, you need to wait for the asynchronous stream by adding done to the test function signature and calling done() on completion or error.
it('should be found by readstream', function _testStream(done){
...
.on('finish', () => done())
.on('error', done)
})