JavaScript async/await 和 do/while 循环

标签 javascript asynchronous async-await do-while ecmascript-2016

当外循环迭代次数少于大约时,以下脚本将按预期运行。 100次。如果外循环迭代几千次,我可以看到我的 console.log 是混合的。例如:

  • 3x 外环输出
  • 1x 内循环输出
  • 1x 外循环输出//不应该发生,因为所有外循环 输出在内循环输出之前!

...或者...

  • 3x 外环输出
  • 2x 内部循环输出//不应该发生,因为只有一个内部循环 循环输出!

...还有很多其他奇怪的组合,但我认为总是相同的原因。

在我的例子中,async/await 和 do/while 循环的组合似乎不能顺​​利工作。我试图通过制作单独的递归函数来摆脱 do/while 循环,但徒劳无功。还有另一种方法可以做到这一点吗?非常感谢任何帮助。

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

编辑:

  • 根据要求,请在下面找到原始代码。
  • 我认为您需要创建一个新的 Google 开发者控制台项目 并插入相应的“clientId”和“apiKey”。
  • 我同时用递归交换了外部 do/while 循环 函数调用,但输出仍然很奇怪。
  • 我不确定如何包含 browser.js 和 runtime.js, 因此脚本标签仍然包含我的路径。
  • 此外,我不确定这是否在代码片段中有效: 第四个脚本标签内的 type="text/babel"src="js/driverights.js"。

"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>

最佳答案

我在 asyncGenerator 函数中没有看到嵌套的 do-while 循环,但我确实看到 asyncGenerator 可以递归调用(有时是间接调用),并且在每次调用都会发生一些新的异步 await。无法保证这些 await 表达式中的任何一个都将以相同的顺序完成,因此无法保证 console.log 语句始终按照它们在代码。您的一些 await 表达式依赖于网络(例如 await listFiles(...)),并且网络并不总是那么可预测。首先开始的请求可能不会先完成,因此您的递归函数调用不会始终按预期执行。

要解决此问题,您可以做的一件事是重构递归调用以也使用 await,因此递归调用可能如下所示:

await asyncGenerator(fileList.result.nextPageToken);

关于JavaScript async/await 和 do/while 循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33522734/

相关文章:

php - 异步返回的附加属性 :false using jQuery

javascript - Observable Race Condition,如何正确计时两个 Observables

javascript - 如何等到整批请求发出并且 promise 得到解决后,才在回调函数上发送数据?

c# - 如何异步设置委托(delegate)类型 UITextField.ShouldReturn?

javascript - 单击多级下拉菜单不显示菜单

android - 如何异步运行 FilterQueryProvider 的查询?

javascript - 在不更改 html 的情况下更改 <li> 文本

python-3.x - Python 异步 io 流

javascript - 如何在包含图片的 Javascript 数组中插入 HTML 标签?

javascript - 通用 AJAX 完成处理程序?