简体   繁体   中英

JavaScript async/await and do/while loop

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 = 'TEST@TEST.COM'; 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> 

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 await s 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 (fe 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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM