1

The following script runs as expected when the outer loop iterates less than approx. 100 times. If the outer loop iterates a few thousand times, I can see that my console.log s are mixed up. For instance:

  • 3x Outer loop output
  • 1x Inner loop output
  • 1x Outer loop output // Should not happen, as all outer loop outputs are before the inner loop output!

...or...

  • 3x Outer loop output
  • 2x Inner loop output // Should not happen, as there is only one inner loop output!

...there are many other weird combinations, but I think it is always the same cause.

It seems that the combination of async/await and do/while loops do not work smoothly in my case. I have tried to get rid of the do/while loops by making separate recursive functions, but in vain. Is there another way of doing that? Any help is very appreciated.

async function asyncGenerator() {
  // other code
  do {
    // other code
    var fileList = await listFiles(nextPageToken);
    // other code
    do {
      // other code
      var parents = await requestParents(fileList.result.items[0].parents[0].id);
      // other code
    } while (response.result.parents[0].isRoot === false);
    // other code
  } while (fileList.result.nextPageToken !== "undefined")
  // other code
}

function listFiles(token) {
  return gapi.client.drive.files.list({
    'maxResults': sizeResults,
    'pageToken': token,
    'q': query
  });
}

function requestParents(fileId) {
  return gapi.client.drive.files.get({
    'fileId': fileId
  });
}

EDIT:

  • As requested, please find below the original code.
  • I think you need to create a new google developers console project and insert the corresponding "clientId" and "apiKey".
  • I exchanged the outer do/while loop in the meantime with a recursive function call, but the output is still strange.
  • I was not sure how to include the browser.js and runtime.js, therefore the script-tags still contain my paths.
  • In addition, I am not sure, if this is working in the snippet: type="text/babel" src="js/driverights.js" within the 4th script-tag.

"use strict";

var driveRights = (function() {
  var clientId = 'YOUR CLIENT ID';
  var apiKey = 'YOUR API KEY';
  var scopes = 'https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/drive.appfolder https://www.googleapis.com/auth/drive.apps.readonly https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.install https://www.googleapis.com/auth/drive.metadata https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/drive.photos.readonly https://www.googleapis.com/auth/drive.scripts';

  function handleClientLoad() {
    var initButton = document.getElementById('init');
    initButton.onclick = function() {
      gapi.client.setApiKey(apiKey);
      window.setTimeout(checkAuth(false, handleAuthResult), 1);
    }
  }

  function checkAuth(imm, callback) {
    gapi.auth.authorize({
      client_id: clientId,
      scope: scopes,
      immediate: imm
    }, callback);
  }

  function handleAuthResult(authResult) {
    if (authResult) {
      gapi.client.load('drive', 'v2', initialize);
    } else {
      $('#progress').html('Anmeldung fehlgeschlagen');
    }
  }

  /////////////////////////////////////////////////////////////////////////////////////////////////////

  var timeOut = 120;
  var counter = 0;
  var tokenMemory;
  var start = new Date().getTime();
  var currentTime;
  // Test data
  var sizeResults = 1;
  var parentFolders = ['0B11RmPttIhB3aFhaMzFQQ0Rjbm8', '0B6R9YDOGf_BUSC0wNW1lRWlnSmc', '0B6R9YDOGf_BUUHRoUW9tRkljUFk', '0B6R9YDOGf_BUfjc3QlZ1YU9Tb2lHcmhLVGhWc3FqSzE4S1dvZlhlLWd6aVFhUWdENWkyYkU'];
  var newUser = '[email protected]';
  var query = 'trashed = false';

  var initialize = function() {
    $('#start').click(function() {
      asyncGenerator();
    });
  };

  async function asyncGenerator(token) {
    try {
        // REQUEST FILES
        counter += sizeResults;
        tokenMemory = token;
        await sleep(timeOut);
        var fileList = await listFiles(token);
        console.log("Requested so far: ", counter);
        console.log("Number of received files: ", fileList.result.items.length);
        console.log(fileList);

        // END REACHED
        if (fileList.result.items.length === 0) {
          console.log("DONE - no more files");
          return;
        }

        // CHECK FILES
        var firstCheckResult = firstCheck(fileList.result.items[0]);
        // Rights
        if (firstCheckResult === "rights") {
          $('#progress').append(`Rechte | ${fileList.result.items[0].title} | ${fileList.result.items[0].owners[0].displayName} | ${fileList.result.items[0].alternateLink} <br>`);
          console.log("TO DO: rights");
        }
        // Check parents
        if (firstCheckResult === "checkParents") {
          var parentID = fileList.result.items[0].parents[0].id;
          do {
            console.log("Do while loop parents are not root");
            await sleep(timeOut);
            var response = await requestParents(parentID);
            parentID = response.result.parents[0].id;
          } while (response.result.parents[0].isRoot === false);
          var secondCheckResult = secondCheck(response);
        }
        // No change
        if (firstCheckResult === "notChange" || secondCheckResult === "notChange") {
          console.log("TO DO: not");
        }
        // Change
        if (firstCheckResult === "change" || secondCheckResult === "change") {
          console.log("TO DO: change");
          await sleep(timeOut);
          await requestPermissions(fileList.result.items[0].id);
        }

        // REFRESH TOKEN IF NECESSARY
        currentTime = new Date().getTime();
        if (currentTime > (start + 2700000)) {
          start = new Date().getTime();
          console.log("Restart asyncGenerator! Reason: Create new token");
          checkAuth(true, asyncGenerator);
        }

        // CHECK IF NEXT PAGE TOKEN EXISTS
        if (typeof fileList.result.nextPageToken !== "undefined") {
          asyncGenerator(fileList.result.nextPageToken);
        } else {
          console.log("DONE - no next page token");
        }

    // RESTART IF ERROR OCCURS
    } catch (err) {
      console.log(err);
      if (err.result.error.code === 500) {
        console.log("Restart asyncGenerator! Reason: Error 500");
        asyncGenerator(tokenMemory);
      }
      if (err.result.error.message.indexOf("Es ist ein interner Fehler aufgetreten, der die Freigabe") > -1) {
        console.log("Restart asyncGenerator! Reason: Permission Error");
        asyncGenerator(tokenMemory);
      }
    }
  }

  function listFiles(token) {
    return gapi.client.drive.files.list({
      'maxResults': sizeResults,
      'pageToken': token,
      'q': query
    });
  }

  function requestParents(fileId) {
    return gapi.client.drive.files.get({
      'fileId': fileId
    });
  }

  function requestPermissions(fileId) {
    return gapi.client.drive.permissions.insert({
      'fileId': fileId,
      'sendNotificationEmails': false,
      'resource': {
        'value': newUser,
        'type': 'user',
        'role': 'writer',
        'name': 'Team'
      }
    });
  }

  function firstCheck(file) {
    // File can't be shared -> output to site
    if (file.writersCanShare === false) {
      return "rights";
    }
    // File is forbidden folder -> do not change
    else if (parentFolders.indexOf(file.id) > -1) {
      return "notChange";
    }
    // File is root-folder and has no parents -> do change
    else if (file.parents.length === 0 && parentFolders.indexOf(file.id) === -1) {
      return "change";
    }
    // Parent-folder of file is root-folder and parent-folder ist not a forbidden folder -> do change
    else if (file.parents[0].isRoot === true && parentFolders.indexOf(file.parents[0].id) === -1) {
      return "change";
    }
    // Parent-folder of file is a forbidden-folder -> do not change
    else if (parentFolders.indexOf(file.parents[0].id) > -1) {
      return "notChange";
    }
    // If none of these exceptions is met -> check parent
    else {
      return "checkParents";
    }
  }

  function secondCheck(file) {
    // If file's parent is one of the forbidden folders-> do not change
    if (parentFolders.indexOf(file.result.id) > -1) {
      return "notChange";
    } else {
      return "change";
    }
  }

  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  return {
    start: handleClientLoad,
  };
})();

driveRights.start();
.cover {
	margin: 5% 0;
	background: none;
}

.full {
  background: url(cover.jpg) no-repeat center center fixed;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  -o-background-size: cover;
  background-size: cover;
}

.coverbox {
  background-color: rgba(255,255,255,0.8) !important;
}

.separator {
  border: 0;
  height: 1px;
  background-image: -webkit-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));
  background-image:    -moz-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));
  background-image:     -ms-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));
  background-image:      -o-linear-gradient(left, rgba(0,0,0,0), rgba(0,0,0,0.75), rgba(0,0,0,0));
}

#init, #start {
	width: 200px;
	margin-top: 10px;
}
<!DOCTYPE>
<html>

<head>
	<title>Drive Rights</title>
	<link rel="stylesheet" href="style.css">
	<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">

	<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
	<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
  
	<script type="text/javascript" src="js/browser.js"></script>
	<script type="text/babel" src="js/driverights.js"></script>
	<script type="text/javascript" src="js/runtime.js"></script>
	<script src="https://apis.google.com/js/client.js"></script>
</head>

<body class="cover full">
	<div class="container">
		<div class="jumbotron coverbox clearfix">
			<h1>Drive Rights</h1>
			<p>
				Change file permissions for specific user.
			</p>
			<hr class="separator">
			<div id="info" class="clearfix">
				<p>
					First click login, then start.
				</p>
				<p class="text-center">
					<button type="button" class="btn btn-primary btn-lg" id="init">
						Login
					</button></br>
					<button type="button" class="btn btn-primary btn-lg" id="start">
						Start
					</button>
				</p>
			</div>
			<div id="progress"></div>
		</div>
</body>

</html>

6
  • No, the loop is hardly the problem, do while works with async/await just as every other loop does (unless its transpilation is broken - can you show us the transpiled ES5 maybe?) Commented Nov 4, 2015 at 13:37
  • Where did you put your log statements? Can you post an example that we can easily run ourselves? Even complete code would help. Commented Nov 4, 2015 at 13:38
  • What does your actual output look like? Commented Nov 24, 2015 at 18:10
  • @trusktr: I changed my second version a little bit and now it is working. I still do not know why the async function did not work in combination with the two do/while loops, but I will keep it in mind and use recursive function calls instead. Commented Nov 24, 2015 at 19:30
  • Your checkAuth function still seems to take a callback instead of returning a promise. Commented Nov 25, 2015 at 7:18

1 Answer 1

1

I don't see a nested do-while loop in your asyncGenerator function, but I do see that asyncGenerator can be called recursively (sometimes indirectly), and during each call some new asynchronous awaits happen. There is no guarantee that any of those await expressions will complete in the same order, and thus no guarantee that the console.log statements always be in order that they happen in the code. Some of your await expressions are relying on the network (f.e. await listFiles(...)) and the network isn't so predictable all the time. It's possible a request that started first might not finish first, so your recursive function calls won't always execute as expected.

One thing you can do to possibly fix this is to refactor your recursive calls to also use await, so the recursive calls might look like:

await asyncGenerator(fileList.result.nextPageToken);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.