I have an async function (a Promise) which does some things. I have to call it N times.
Every call represents a simulation point.
My first guess was to use a loop:
for(let i=0; i < N; i++) {
myAsyncFunc(data[i])
.then(() => myAsyncFunc(data[i]) )
}
Obviously, this does not work because the loops and before any subsequent call to myAsyncFun.
How can I call step-by-step the async function, waiting for results and proceed to the next step?
I tried whit this:
function myAsyncFunc(data) {
return new Promise( (resolve, reject) => {
anotherAsync.then(resolve).catch(reject);
}
}
function simulate(mode) {
[...Array(10)].reduce((p, _, i) =>
p.then(_ => new Promise(resolve => {
myAsyncFunc(data[i]); // <== this return a Promise
}
))
, Promise.resolve());
}
But the functions myAsyncFunc are not called in sequence.
I solved by using async/await which, apparently, seems to solve the acrobatics with asynchronous function calls details of JS
function myAsyncFunc(data) {
anotherAsync.then( () => return );
}
async function simulate(mode) {
for(let i=0; i < tempModel[0].linear.length; i++)
{
let a = await myAsyncFunc(mode,i);
}
}
Related
I have an async method which invokes a few calls (await promise....) in a loop,
and eventually returns a result.
When I get the result from that function, I send back a response to the client.
What I have to do is to return a response in the middle of the process/loop,
while the loop continue running.
Of course it breaks the loop, since it returns from the function in the middle of the loop.
Here is a piece of code to demonstrate.
app.post('/test', async (req, res)=>{
const body = JSON.stringify(req.body)
await doSomething()
res.status(200).send("ok");
});
const doSomething = async () => {
let times = 0;
for(let i=0 ; i<5 ; i++){
console.log(i)
await delay(1000);
if(i==2){
return 2;
}
}
return times;
}
const delay = async (ms) => {
return await new Promise(resolve => setTimeout(resolve, ms));
}
When I get i==2, I want to return a value and response to the client,
but I still want the loop to run till the end.
I thought to use some observer which will be responsible for returning a response,
(say rxjs, event emitter, etc...). Not sure what is the best practice for such
a different situation.
Thanks for any advice.
The solution is to use a synchronous function and return a Promise. But AFAIK making the callback passed to the Promise constructor async won't work:
const doSomething = () => {
let times = 0;
return new Promise(async (resolveMain) => {
for(let i=0 ; i<5 ; i++){
console.log(i)
await delay(1000);
if(i==2){
resolveMain(2);
}
}
});
}
It's also twisted LOL.
So I'm sure there are many other ways to accomplish this, but I'm assuming you want to keep the await delay(1000);. What you're gong to need is an async generator function.
app.post('/test', async (req, res)=>{
const body = JSON.stringify(req.body)
await doSomething()
res.status(200).send("ok");
});
async function* doLoopyThing(){
let times = 0;
for(let i=0 ; i<5 ; i++){
console.log(i)
await delay(1000);
if(i==2){
yield 2;
}
}
}
function doSomething(){
return new Promise(resolve => {
const looper = doLoopyThing();
looper.next().then(({value}) => resolve(value));
looper.next();
}
}
This is also twisted, but it should work.
I don't understand how this can possibly be the right way to actually accomplish something. But I'm assuming the is just for fun, or you're trying to learn.
Also if all you were trying to show with the await delay(1000) was a heavy function, and you don't actually need the function to be asynchronous, the first solution should work.
I am trying to work with recursion in asynс/await functions. The problem is that I cannot get the final promise in the main function, in which I initially call the recursive method
async function delay(ms) {
return await new Promise(resolve => setTimeout(resolve, ms))
}
async function recursion(i) {
return new Promise(async (resolve, reject) => {
if (i == 0) {
console.log(`i == 0`)
resolve(i)
} else {
console.log(`i = ${i}. Wait 1 second...`)
i--
await delay(2000)
await recursion(i)
}
})
}
async function main() {
let i = await recursion(3)
console.log(`END OF RECURSION`) //This code never use!
console.log(`i => ${i}`)
}
main()
console.log:
i = 3. Wait 1 second...
i = 2. Wait 1 second...
i = 1. Wait 1 second...
i == 0
This is promise construction antipattern. There's already existing promise to chain, no need to create a new one. A promise that is returned from Promise callback is ignored, this breaks promise chain. Also, there is an inconsistency, the value isn't always returned from recursion.
It should be:
async function recursion(i) {
if (i == 0) {
console.log(`i == 0`)
return i
} else {
console.log(`i = ${i}. Wait 1 second...`)
i--
await delay(2000)
return recursion(i)
}
}
I am trying to use the forEach() function in the node js net library in my tcp event but it is completely failing to run what is going wrong
I have made a async function to replace forEach() but it still wont work even tho i know the function is being called
async function asyncForEach(array, callback) {
console.log('async function')
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
nothing happens except the expected console.logs this is how i am using the function
sock.on('data',function(data){
var data = Buffer.from(data).toString();
var arg = data.split(',');
var event = arg[0];
if(event == 'stdout'){
console.log('stdout')
asyncForEach(controlClients, async (num) => {
await waitFor(50);
console.log(num);
});
}
});
i am expecting the return each row of the array here so i can do something for a selected controlClient any help?
It's a little hard to figure out exactly what your full environment is. When I run this code:
const waitFor = (ms) => new Promise(r => setTimeout(r, ms));
async function asyncForEach(array, callback) {
console.log('async function')
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
let controlClients = [1,2,3,4];
function test() {
console.log('stdout')
return asyncForEach(controlClients, async (num) => {
await waitFor(50);
console.log(num);
});
}
test().then(() => {
console.log("done");
}).catch(err => {
console.log(err);
});
I get this output:
stdout
async function
1
2
3
4
done
Hopefully you can take this as a starting point and figure out what is going wrong in your particular environment. Note, you should always have a .catch() on pretty much any operation that involves a promise so you can see if there are any errors in your promise chain.
And, since you don't show your socket code so I can't reproduce that part of it, I simplified to just run the rest of the code once in a small test app.
I have a problem when i use q promise in loop. Result show
0
1
2
--
--
--
But result that i promise must be:
0
--
1
--
2
--
There is my code:
for(let i = 0; i < 3; i++) {
let row = planRespone[i];
let planData = {
diary_id:diaryData.id,
title:row.title
};
console.log(i);
addDiaryPlan(planData)
.then((insertId) => {
console.log("--");
})
.catch((err) => {
throw err;
})
};
And support fot it
let addDiaryDetail = (data) => {
let q = Q.defer();
Mdl.addDiaryDetail(data, function(err, result) {
if(err) q.reject(err);
else q.resolve(result);
});
return q.promise;
}
How can i use promise in this case?
As others have said, I don't see any need for Q at all. You can do this with node.js built-in functionality (util.promisify()).
And when you want to run a for loop in sequential order where you wait for an async operation to be done before going to the next iteration of the loop, then async/await is the easiest way to do things because it will pause the for loop:
const util = require('util');
Mdl.addDiaryDetailPromise = util.promisify(Mdl.addDiaryDetail);
async function someFunction() {
for (let i = 0; i < 3; i++) {
let row = planRespone[i];
let planData = {
diary_id:diaryData.id,
title:row.title
};
console.log(i);
await Mdl.addDiaryPlanPromise(planData).then(insertId => {
console.log("--");
});
};
}
// usage
someFunction().then(() => {
console.log("all done");
}).catch(err => {
console.log(err);
});
This should give you the desired sequence of output.
Note, that to use await, it has to be inside a function that is declared async and that function will always return a promise that resolves when the function is done or rejects when there's some sort of uncaught error in the function (an actual exception or a rejected await).
I am trying to control the flow of the execution in my code below, meaning I want it to be serial.
I am reading and updating data from and to my DB, and ofc I want that to happen in the correct order. Below is the function I am calling my DB from, the queries functions are wrapped in callbacks.
I am pretty new to promises so perhaps the error might be something silly I am overlooking. If you need anything to ask please do so.
function my_function(array, array2)
{
var array3 = [];
return Promise.resolve(true)
.then(function()
{
console.log("1")
for(var i=0; i< array.length; i++)
{
get(array[i], function(results){
console.log("2")
array3.push(..);
});
}
return array3;
}).then(function()
{
console.log("3")
for(var i=0; i< array2.length; i+=2)
{
//...
get(array2[i], function(results){
console.log("4")
return array3.push(...);
});
}
return array3;
}).then(function(array3)
{
console.log("5")
for(var i=0; i<array3.length; i++)
{
get(array3[i], function(results){
console.log("6")
update(.., function(callb_result){
return;
});
});
}
});
}
And here is the way I am calling the queries.
function get(array, callback)
{
db.get(`SELECT .. FROM .. WHERE ..;`, function(error, row) {
...
return callback(something);
});
}
function update(.., callback)
{
db.run(`UPDATE .. SET ...`);
return callback("updated"); //I dont want to return anything
}
Whats printed in the log
1
3
5
2
4
6
I was thinking perhaps the way I ma calling the queries is async and that's messing up everything.
You're using for loops to run asynchronous tasks and return an array that is modified by them. But because they are asynchronous the return happens before they are finished. Instead you can create an array of promises where each promise is one of the asynchronous tasks that resolves once the task is done. To wait until every task is done you can call Promise.all with the array of promises, which returns a promise that resolves with an array of the resolved results.
For the first .then you can use Array.prototype.map to easily create an array of promises. Each item in the array needs to return a new Promise that resolves with the result from the callback of get.
.then(function() {
console.log("1");
const promiseArray = array.map(function(item) {
return new Promise(function(resolve) {
get(item, function(result) {
console.log("2");
resolve(result);
});
});
});
return Promise.all(promiseArray);
})
As you return Promise.all the next .then call be executed once all the promises in the promiseArray are fulfilled. It will receive the array of results as the first parameter to the function. That means you can use them there. The second .then is similar to the first one, except that you don't want to call get on every item. In this case map is not applicable, so the for loop will just create a promise and add it to the array of promises. Before you have used array3 to store the results that you want to update, but with promises you don't really need that. In this case you can simply concat the results of both arrays.
.then(function(resultsArray) {
console.log("3");
const promiseArray2 = [];
for (var i = 0; i < array2.length; i += 2) {
const promise = new Promise(function(resolve) {
get(array2[i], function(results) {
console.log("4");
resolve(results);
});
});
promiseArray2.push(promise);
}
// Wait for all promises to be resolved
// Then concatenate both arrays of results
return Promise.all(promiseArray2).then(function(resultsArray2) {
return resultsArray.concat(resultsArray2);
});
})
This returns a promise that resolves with the concatenated array, so you will have all the results (from both .then calls) as an array, which is passed to the next .then function. In the third and final .then you simply call update on each element of the array. You don't need to call get again, as you've already done this and you passed on the results.
.then(function(finalResults) {
console.log("5");
for (var i = 0; i < finalResults.length; i++) {
console.log("6");
update(finalResults[i], function(result) {
console.log(result);
});
}
});
Full runnable code (get uses a timeout to simulate asynchronous calls)
function myFunction(array, array2) {
return Promise.resolve(true)
.then(function() {
console.log("1");
const promiseArray = array.map(function(item) {
return new Promise(function(resolve) {
get(item, function(results) {
console.log("2");
resolve(results);
});
});
});
return Promise.all(promiseArray);
})
.then(function(resultsArray) {
console.log("3");
const promiseArray2 = [];
for (var i = 0; i < array2.length; i += 2) {
const promise = new Promise(function(resolve) {
get(array2[i], function(results) {
console.log("4");
resolve(results);
});
});
promiseArray2.push(promise);
}
return Promise.all(promiseArray2).then(function(resultsArray2) {
return resultsArray.concat(resultsArray2);
});
})
.then(function(finalResults) {
console.log("5");
for (var i = 0; i < finalResults.length; i++) {
console.log("6");
update(finalResults[i]);
}
});
}
function get(item, cb) {
// Simply call the callback with the item after 1 second
setTimeout(() => cb(item), 1000);
}
function update(item) {
// Log what item is being updated
console.log(`Updated ${item}`);
}
// Test data
const array = ["arr1item1", "arr1item2", "arr1item3"];
const array2 = ["arr2item1", "arr2item2", "arr2item3"];
myFunction(array, array2);
Improving the code
The code now works as expected, but there are many improvements that make it a lot easier to understand and conveniently also shorter.
To simplify the code you can change your get function to return a promise. This makes it a lot easier, since you don't need to create a promise in every step. And update doesn't need to be a promise, neither does it need a callback as it's synchronous.
function get(array) {
return new Promise(function(resolve, reject) {
db.get(`SELECT .. FROM .. WHERE ..;`, function(error, row) {
if (err) {
return reject(error);
}
resolve(something);
});
});
}
Now you can use get everywhere you used to create a new promise. Note: I added the reject case when there is an error, and you'll have to take care of them with a .catch on the promise.
There are still too many unnecessary .then calls. First of all Promise.resolve(true) is useless since you can just return the promise of the first .then call directly. All it did in your example was to automatically wrap the result of it in a promise.
You're also using two .then calls to create an array of the results. Not only that, but they perform exactly the same call, namely get. Currently you also wait until the first set has finished until you execute the second set, but they can be all executed at the same time. Instead you can create an array of all the get promises and then wait for all of them to finish.
function myFunction(array, array2) {
// array.map(get) is equivalent to array.map(item => get(item))
// which in turn is equivalent to:
// array.map(function(item) {
// return get(item);
// })
const promiseArray = array.map(get);
for (let i = 0; i < array2.length; i += 2) {
promiseArray.push(get(array2[i]));
}
return Promise.all(promiseArray).then(results => results.forEach(update));
}
The myFunction body has been reduced from 32 lines of code (not counting the console.log("1") etc.) to 5.
Runnable Snippet
function myFunction(array, array2) {
const promiseArray = array.map(get);
for (let i = 0; i < array2.length; i += 2) {
promiseArray.push(get(array2[i]));
}
return Promise.all(promiseArray).then(results => results.forEach(update));
}
function get(item) {
console.log(`Starting get of ${item}`);
return new Promise((resolve, reject) => {
// Simply call the callback with the item after 1 second
setTimeout(() => resolve(item), 1000);
});
}
function update(item) {
// Log what item is being updated
console.log(`Updated ${item}`);
}
// Test data
const testArr1 = ["arr1item1", "arr1item2", "arr1item3"];
const testArr2 = ["arr2item1", "arr2item2", "arr2item3"];
myFunction(testArr1, testArr2).then(() => console.log("Updated all items"));