最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

loops - Iterating javascript object synchronously - Stack Overflow

programmeradmin2浏览0评论

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
 |  Show 2 more ments

5 Answers 5

Reset to default 2

You 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.

发布评论

评论列表(0)

  1. 暂无评论