I have an object like so:
let myObject = {
'db1': [db1_file1Id,db1_file2Id,db_1file3Id],
'db2': [db2_file1Id, db2_file2Id]
...
}
I iterate through through this object and on each iteration: I connect to the database, retrieve the file, do some stuff and save the file back. Basically asynchronous stuff.
for (let prop in myObject) {
if (myObject.hasOwnProperty(prop)) {
doStuff(prop, myObject[prop]);
}
}
Now the doStuff function makes sure I have a local scope so there is no inconsistencies. But still, the execution is not synchronous due to the asynchronous operations inside each loop. I basically need one db to be pletely processed before moving on to the next. How do I fix this?
One approach that I thought of was to have recursive loop. But as per my understanding, this would require me to change my data structure extensively which is sub-optimal imo.
let arr; //containing my data
process(x) {
if (x < arr.length){
//process(x+1) is called inside doStuff after asynchronous operations are plete
doStuff(arr[x]);
}
}
I have an object like so:
let myObject = {
'db1': [db1_file1Id,db1_file2Id,db_1file3Id],
'db2': [db2_file1Id, db2_file2Id]
...
}
I iterate through through this object and on each iteration: I connect to the database, retrieve the file, do some stuff and save the file back. Basically asynchronous stuff.
for (let prop in myObject) {
if (myObject.hasOwnProperty(prop)) {
doStuff(prop, myObject[prop]);
}
}
Now the doStuff function makes sure I have a local scope so there is no inconsistencies. But still, the execution is not synchronous due to the asynchronous operations inside each loop. I basically need one db to be pletely processed before moving on to the next. How do I fix this?
One approach that I thought of was to have recursive loop. But as per my understanding, this would require me to change my data structure extensively which is sub-optimal imo.
let arr; //containing my data
process(x) {
if (x < arr.length){
//process(x+1) is called inside doStuff after asynchronous operations are plete
doStuff(arr[x]);
}
}
Share
Improve this question
asked Dec 9, 2017 at 4:49
Priyath GregoryPriyath Gregory
9871 gold badge13 silver badges43 bronze badges
7
- @leaf You can create a serial promise chain from an array using recursion or Array.prototype.reduce. I added as answer. – HMR Commented Dec 9, 2017 at 6:44
- Do you want to process the databases in a specific order ? – user1636522 Commented Dec 9, 2017 at 8:02
- @leaf the ordering does not matter – Priyath Gregory Commented Dec 9, 2017 at 8:08
- If the order doesn't matter then why does it need to be serialized? Does the next db function need the output of the previous one? – HMR Commented Dec 9, 2017 at 8:17
- @HRM its precautionary to prevent any sort of server overloading – Priyath Gregory Commented Dec 9, 2017 at 8:35
5 Answers
Reset to default 2You could use the solution you proposed at the end using Object.entries(obj)
. For example,
let arrProps = Object.entries(myObject);
process(index) {
if (index < arrProps.length){
// Call the callback once you plete execution of doStuff
doStuff(arrProps[index], () => process(index + 1));
}
}
Inside doStuff:
function doStuff(props, callback) {
// Process props
//finally in the promise of async call, on success call
.then(callback)
}
OR you could use a generator function, if you want to use for ... in
loop.
The following will do what you ask, it returns an array of resolve values.
Do you want to stop processing if any one of them rejects? In case you need to make some changes, now it rejects if any of them reject and won't continue processing they keys in your object (object named myObject
):
var myObject = {
'one': ["one"],
'two': ["two"]
};
var doStuff = arr =>
console.log("starting:", arr[0]) ||
Promise.resolve(arr[0]);
var [promise,result] =
Object.keys(myObject)
.reduce(
([promise,results], key) =>
[
promise
.then(
resolve =>
doStuff(myObject[key])
)
.then(
resolve => results.push(resolve)&&resolve
)
.then(
resolve => console.log("done:", resolve)
)
,results
]
, [Promise.resolve(), []]
)
promise.then(
_ => {
console.log("done all",result)
}
);
The answer ayushgp uses recursion, here is a working example that doesn't need changes to doSomething:
var myObject = {
'one': ["one"],
'two': ["two"]
};
var doStuff = arr =>
console.log("starting:",arr[0]) ||
Promise.resolve(arr[0])
var process = (arr,processFn) => {
const rec = (arr,processFn,promise,results) =>
arr.length === 0
? promise.then(_=>results)
: promise
.then(_ => processFn(arr[0][1]))
.then(result=>results.push(result)&&console.log("resolved:",result))
.then(_ => rec(arr.slice(1),processFn,promise,results));
return rec(arr,processFn,Promise.resolve(),[]);
};
process(
Object.keys(myObject).map(key=>[key,myObject[key]]),
doStuff
)
.then(
results => console.log("done all, results:",results)
);
One solution would be to make doStuff
return a Promise
which you can use to build a chain of promises using calls to then
.
The Bluebird promise library provides this functionality with .each
and .mapSeries
.
You could implement it as:
Promise.forEachSeries = function(array, action) {
return array.reduce(function(prevPromise, value, index, array) {
return prevPromise.then(function() {
return action(value, index, array);
});
}, Promise.resolve());
}
You would use it like this:
Promise.forEachSeries(arr, doStuff);
The following code might be close to what you are asking. I use indices i
and j
to loop through databases and files respectively :
var dbs = {
db1: ["q", "w", "e", "r"],
db2: ["t", "y"]
};
var names = Object.keys(dbs);
var db, x, i = 0, j = 0;
if (names.length > 0) {
db = dbs[names[i]];
x = db[j];
console.log("start");
asyncProcessing(x)
.then(onSuccess)
.catch(onFailure);
}
function onFailure (e) {
console.log("[FAILURE]", e);
console.log("end");
}
function onSuccess (xx) {
console.log("[SUCCESS]", xx);
j = (j + 1) % db.length; // next j
if (j === 0) i = i + 1; // next i
if (i < names.length) {
db = dbs[names[i]];
x = db[j];
asyncProcessing(x)
.then(onSuccess)
.catch(onFailure);
} else {
console.log("end");
}
}
function asyncProcessing (x) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
// force first two success then random
if (x === "q" || x === "w" || Math.random() * 3 > 1) {
resolve(x + x);
} else {
reject("Not lucky. Try again.");
}
}, 1000);
});
}
The Promise object represents the eventual pletion (or failure) of an asynchronous operation, and its resulting value. you can try like .
$("#myPara").delay(4500).fadeOut().promise().done(function(){
$("#myHeading").attr("style","display:none;") ;
for(var i=10;i<15;i++){
console.log(i);
}
});
console.log("Hello promise !");
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="myPara"> Hello </p>
<h1 id="myHeading">to be hide</h1>
for (let prop in myObject) {
if (myObject.hasOwnProperty(prop)) {
var stuff= doStuff(prop, myObject[prop]).promise().done(function(){
// Do whatever u want after pletion of doStuff
});
}
}
Have a look at Mozila ref.