I have four task that are working in sync.
It looks like this:
function createEntity(req, res, next) {
validator.validateInternalProductRequest(req.body)
.then((data) => feedbackSetting.getUrl(data))
.then((data) => bitlyService.shortenLink(data))
.then((data) => internalProductService.saveInternalProductRequest(data))
.then((data) => res.send(data))
.catch(err => {
console.log(err);
next(err)
});
}
Now between 3rd and 4th task, that is after getting short link and before saving to the database, I need to perform a task, lets say Task AS that I need to run asynchronously. The 4th task, i.e saving to the database should not be blocked because of this.
Now this task AS that I have to do asynchronously, has further three more task:
1. getting setting from db
2. making curl request
3. saving in the database
These three task I can do using async.waterfall or I am hoping there would be any alternatives to this?
How do I perform this task AS in the above mentioned function createEntity?
If you do not want to wait for the async task, just call it and dont wait for it. It is easy as that.
function createEntity(req, res, next) {
validator.validateInternalProductRequest(req.body)
.then((data) => feedbackSetting.getUrl(data))
.then((data) => {
callSomeAsyncMethodAndDontWaitForResult();
return bitlyService.shortenLink(data)
}).then((data) => internalProductService.saveInternalProductRequest(data))
.then((data) => res.send(data))
.catch(err => {
console.log(err);
next(err)
});
}
Related
I want to delete product detail #
Below API is used to call the node.js API ##
When a button is pressed then deleteApi is called then this will be matched with the backend
const deleteApi = (id) => {
console.log(id);
axios
.delete(`http://localhost:5000/delete-list/${id}`)
.then((res) => {
console.log("Deleted Iteam", res);
})
.error((error) => {
console.log(error);
});
};
The problem here is that you need to use .catch instead of .error, change it.
postsArr does not get data
router.get('/user-post/:id', checkJwt, (req, res, next) => {
let postsArr = []
db.userSchema.findOne({ _id: req.params.id })
.populate('posts')
.exec((err, da) => {
for (let i = 0; i < da.posts.length; i++) {
db.postSchema.find({ _id: da.posts[i]._id })
.populate('comments')
.exec((err, post) => {
postsArr.push(post)
})
}
console.log(postsArr)
})
})
This is a whole lot easier if you use the promise interface on your database:
router.get('/user-post/:id', checkJwt, async (req, res, next) => {
try {
let da = await db.userSchema.findOne({ _id: req.params.id }).populate('posts').exec();
let postsArray = await Promise.all(da.posts.map(post => {
return db.postSchema.find({ _id: post._id }).populate('comments').exec();
}));
res.json(postsArray);
} catch (e) {
console.log(e);
res.sendStatus(500):
}
});
The challenge with an asynchronous operation in a loop is that they don't run sequentially - they all run in parallel. The for loop just starts all your asynchronous operations and then you never know when they are all done unless you track them all somehow. That can be done without promises by using counters to keep track of when every single asynchronous result is done, but it's a whole lot easier to just let Promise.all() do that for you. It will also put all the results in the right order for you too.
If you wanted to sequence the database operations and run them serially one at a time, you could do this:
router.get('/user-post/:id', checkJwt, async (req, res, next) => {
try {
let da = await db.userSchema.findOne({ _id: req.params.id }).populate('posts').exec();
let postsArray = [];
for (let post of da.posts) {
let result = await db.postSchema.find({ _id: post._id }).populate('comments').exec();
postsArray.push(result);
}
res.json(postsArray);
} catch (e) {
console.log(e);
res.sendStatus(500):
}
});
This second version runs only one database operation at a time, sequentially. It will put less peak load on the database, but likely be slower to finish.
You will notice that the use of promises and await makes the error handling much simpler too as all errors will propagate to the same try/catch where you can log the error and send an error response. Your original code did not have error handling on your DB calls.
I'm not experienced with Javascript promises and recently I started using promises instead of callbacks in my Javascript projects.
When I tried to run several promise functions one after another I landed in a nested chaos of then(). The code works exactly as expected, but my question is that if this is the way to resolve several promise functions one after another then what is the advantage of using promises instead of callbacks.
If I'm not doing it the right way, then it is a request from you guys to show me the proper way of resolving nested promises.
Below is my code that I don't like it they way it looks:
exports.editExpense = (req, res, next) => {
Account.findAll().then(accounts => {
Budget.findAll().then(budgets => {
Expense.findAll().then(expenses => {
Expense.findByPk(id).then(expense => {
res.render('expenses/index', {
urlQuery: urlQuery,
expenses: expenses,
expense: expense,
accounts: accounts,
budgets: budgets
});
})
})
})
}).catch(error => console.log(error));
};
You can use async/await structure for better formatting
exports.editExpense = async(req, res, next) => {
try {
let accounts = await Account.findAll();
let budgets = await Budget.findAll();
let expenses = await Expense.findAll()
let expense = await Expense.findByPk(id);
if (expense) {
res.render('expenses/index', {
urlQuery: urlQuery,
expenses: expenses,
expense: expense,
accounts: accounts,
budgets: budgets
});
} else {
console.log('else') //<<- Render/Handle else condition otherwise server will hang.
}
} catch (error) {
console.error(error)
}
You should try to minimize the amount of async calls you make in a function as it will impact your performance.
If you prefer to use the then catch structure, in order to take fully advantage of it I recommend you not to nest them. Of course you can, but then you should put a .catch() after each of them. That's why the async introduction made an easier code to read and handle errors, as it simplifies it with the try catch structure.
If you pipe multiple .then(), you can return a value as a promise from each of them that can be used inside the next one once the promise resolves. The only thing is that you loose these values unless you save them either in req with new properties or in variables declared outside the pipe of .then().
That's why, in this snippet, I declared all the variables at the beginning in order to save all the values and use them in the final res
exports.editExpense = (req, res, next) => {
let accounts;
let budgets;
let expenses;
Account.findAll()
.then(fetchedAccounts => {
accounts = fetchedAccounts;
return Budget.findAll()
})
.then(fetchedBudgets => {
budgets = fetchedBudgets;
return Expense.findAll()
})
.then(fetchedExpenses => {
expenses = fetchedExpenses
return Expense.findByPk(id)
})
.then(expense => {
return res.render('expenses/index', {
urlQuery: urlQuery,
expenses: expenses,
expense: expense,
accounts: accounts,
budgets: budgets
});
})
.catch(error => console.log(error));
};
Straight forward: I have a backend with multiple routes.
Is it possible to return multiple queries from one route, e.g.:
router.get("/test/:id", (req, res) => {
example.find({test: req.params.id})
.then(data => res.json(data.map(moreData => moreData.serialize()))
differentExample.find({something: req.params.id})
.then(data => res.json(data.map(moreData => moreData.serialize()))
})
And then have both finds/results in the front-end:
.then(res => {
if (!res.ok) {
return Promise.reject(res.statusText);
}
return res.json();
})
.then(data => console.log(data))
I found that if I user Promise.all() I use the result of the first query for the query parameters of the second. The above is just my thought process. It doesn't work like this and I am missing the piece that makes it happen (if it's possible)
You can't send multiple responses using HTTP. HTTP requests to responses are 1:1 so when a request is sent you will ALWAYS expect only one response. Anything else and it would get quickly messy. So, how to send multiple sets of data?
You could do something like this:
router.get("/test/:id", (req, res) => {
let result = {}
example.find({test: req.params.id})
.then(data => {
result['partOne'] = data.map(moreData => moreData.serialize())
return differentExample.find({something: req.params.id})
}).then(data => {
result['partTwo'] = data.map(moreData => moreData.serialize())
res.json(result)
})
}
Note: I haven't tested any of this code. But in essence, do both requests, and once you have the result of both requests you can return the result. If the requests don't depend on each other, you could use something like this using Promise.all as you mentioned:
Promise.all(
example.find({test: req.params.id},
differentExample.find({something: req.params.id})
).then(result => {
res.json(result);
})
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.