Iterating javascript object synchronously

最后都变了- 提交于 2020-01-05 08:40:51

问题


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 completely 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 complete
   doStuff(arr[x]);
 }
}

回答1:


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)
);



回答2:


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);



回答3:


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




回答4:


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);
  });
}



回答5:


The Promise object represents the eventual completion (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.com/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 completion of doStuff
     });
    }
   }

Have a look at Mozila ref.



来源:https://stackoverflow.com/questions/47725420/iterating-javascript-object-synchronously

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!