簡體   English   中英

JavaScript:使用 Promise 在循環中更新 DOM

[英]JavaScript : updating DOM in loop with Promise

這已經困擾了我幾個星期了。 我有一些 Javascript 的工作示例,它在循環中更新 DOM。

但是我無法讓這個“技巧”適用於我的真實代碼。

這是我的 Plunk 的鏈接: https ://plnkr.co/edit/oRf6ES74TJatPRetZEec ? p = preview

HTML:

<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
    <link rel="stylesheet" href="style.css">
    <script src="http://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="http://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
    <script src="js/script.js"></script>
    <script src="js/script2.js"></script>
    <script src="js/json-util.js"></script>
    <script src="js/scorito-dal.js"></script>
    <script src="js/matching.js"></script>
  </head>

  <body>
    <div id="log">..log here..</div>
    <button onclick="doHeavyJob();">do heavy job</button>
    <button onclick="doHeavyJobJquery();">do heavy job with jQuery</button>
    <button onclick="match();">match</button>
  </body>

</html>

js/script.js 中的腳本,從“做繁重的工作”按鈕調用的工作:

async function doHeavyJob() {
  $('#log').html('');
  for (var j=0; j<10; j++) {
    await sleep(1000)
    console.log('Iteration: ' + j);
    (function (j) {
        setTimeout(function() { // pause the loop, and update the DOM
            var logPanel = document.getElementById('log');
            var txt = 'DOM update' + j;
            logPanel.innerHTML += txt + ', ';
        }, 0);
    })(j);
  }
}

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

完全相同的代碼在 jQuery each() 中不起作用,這似乎是由於 jQuery,並且可以通過使用 for 循環輕松解決。 如果您有興趣,請查看我的 plunk 中的 script2.js。

我在 js/matching 中的真實腳本(從“匹配”按鈕調用)不起作用。

var _keeperCombinations = [];
var _defenderCombinations = [];
var _midfielderCombinations = [];
var _attackerCombinations = [];
var _mostPoints = 0;
var _bestCombination = [];

function updateStatus(keeperCount, ixKeeper, msg) {
    msg = '%gereed: ' + Math.round(ixKeeper / keeperCount * 100, 0);
    console.log(msg);
    $('#log').html(msg);
}

function match() {
  $('#log').html('');
  updateStatus(1, 1, 'Match started');
  var playersData = scoritoDal.getPlayersData();
  this.determineKeepers(playersData);
  this.determineDefenders(playersData);
  this.determineMidfielders(playersData);
  this.determineAttackers(playersData);
  var keeperCount = _keeperCombinations.length
  for (var ixKeeper=0; ixKeeper<keeperCount; ixKeeper++) {
    var keepers = _keeperCombinations[ixKeeper]; 
    doMatching(keepers, keeperCount, ixKeeper);
  }

  if (_bestCombination.length === 0) {
    alert('Er kon geen beste combinatie worden bepaald');
  }
  else {
    alert('Ready: ' + _bestCombination.toString());
    $(_bestCombination).each(function(ix, playerId) {

    });
  }
}

/*
* Match 2 keepers, 5 defenders, 6 midfielders and 5 attackers
* First pick the 5 best keepers, 10 best defenders, 12 best midfielders and 10 best attackers.
* 3 / 2 (k), 7 / 3 (d & a) and 8 / 4 (m) >> most points / most points per prices
*/
var _confirmed = false;
function doMatching(keepers, keeperCount, ixKeeper) {

  // Make a new promise
  let p = new Promise(
    // The executor function is called with the ability to resolve or reject the promise
   (resolve, reject) => {
     for (var ixDefenders=0; ixDefenders<_defenderCombinations.length; ixDefenders++) {
        var defenders = _defenderCombinations[ixDefenders]; 
        for (var ixMidfielders=0; ixMidfielders<_midfielderCombinations.length; ixMidfielders++) {
          var midfielders = _midfielderCombinations[ixMidfielders]; 
          for (var ixAttackers=0; ixAttackers<_attackerCombinations.length; ixAttackers++) {
            var attackers = _attackerCombinations[ixAttackers];
            procesPlayers(keepers, defenders, midfielders, attackers);
        }
      }
      resolve(ixDefenders);
    }
  });
  p.then(
    function(ixDefenders){
      console.log('LOG: ' + keeperCount + " - " + ixKeeper);
      updateStatus(keeperCount, ixKeeper);
    }
  );
}

async function procesPlayers(keepers, defenders, midfielders, attackers) {
  var totals = calculateTotals(keepers, defenders, midfielders, attackers);
  // check if total price is within budget
  if (totals.TotalPrice <= 56500000) {
      if (totals.TotalPoints > _mostPoints) {
          _mostPoints = totals.TotalPoints;
          setBestCombination(keepers, defenders, midfielders, attackers);
      }
  }
}

function calculateTotals(keepers, defenders, midfielders, attackers) {
  var playerIds = [];
  var totalPoints = 0;
  var totalPrice = 0;
  var allPlayers = keepers.concat(defenders, midfielders, attackers);
  var checkTeams = [];
  $(allPlayers).each(function(ix, player){
      var club = checkTeams.find(t => t.Name === player.Club);
      if (!club) {
          club = {"Name":player.Club, "Count":1};
          checkTeams.push(club);
      }
      else club.Count++;
      if (club.Count > 4) {
          totalPoints = 0;
          return false;
      }
      playerIds.push(player.ID);
      var factor = 1;
      if (player.Position === 'Keeper' && ix > 0) factor = 0.5; // 2nd keeper gets less points
      if (player.Position === 'Defender' && ix > 2) factor = 0.5; // 4th defender gets less points
      if (player.Position === 'Midfielder' && ix > 3) factor = 0.5; // 5th midfielder gets less points
      if (player.Position === 'Attacker' && ix > 2) factor = 0.5; // 4th attacker gets less points
      var playerPoints = player.Points * factor;

      totalPoints += playerPoints;
      totalPrice += player.Price;
  });
  return {"TotalPoints":totalPoints,"TotalPrice":totalPrice};
}

function determineKeepers(playersData) {
  console.log('Determining keepers');
  $('#progres').text('Determine 5 best keepers');
  var bestKeepers = this.determineBestPlayers(playersData, 'Keeper', 3, 2); // 3, 2
  if (bestKeepers && $(bestKeepers).length > 0) {
      // now determine all combinations
      this.determineCombinations(bestKeepers, 2);
      _keeperCombinations = this._combinations;
  }
}

function determineDefenders(playersData) {
  console.log('Determining defenders');
  $('#progres').text('Determining 10 best defenders');
  var bestDefenders = this.determineBestPlayers(playersData, 'Defender', 5, 3); // 6, 3
  if (bestDefenders && $(bestDefenders).length > 0) {
      // now determine all combinations
      this.determineCombinations(bestDefenders, 5);
      _defenderCombinations = this._combinations;
  }
}

function determineMidfielders(playersData) {
  console.log('Determining midfielders');
  $('#progres').text('Determine 12 best midfielders');
  var bestMidfielders = this.determineBestPlayers(playersData, 'Midfielder', 5, 3); // 7, 3
  if (bestMidfielders && $(bestMidfielders).length > 0) {
      // now determine all combinations
      console.log('*** Determining all midfielder combinations ***');
      this.determineCombinations(bestMidfielders, 6);
      _midfielderCombinations = this._combinations;
  }
}

function determineAttackers(playersData) {
  console.log('Determining attackers');
  $('#progres').text('Determine 10 best attackers');
  var bestAttackers = this.determineBestPlayers(playersData, 'Attacker', 5, 3); // 6, 3
  if (bestAttackers && $(bestAttackers).length > 0) {
      // now determine all combinations
      this.determineCombinations(bestAttackers, 5);
      _attackerCombinations = this._combinations;
  }
}

/*
* Determine the best players for a position
* - position: Keeper|Defender|Midfielder|Attacker
* - byPoints: the nr of best players by points
* - byFactor: the nr of best players by factor
*/
function determineBestPlayers(playersData, position, byPoints, byFactor) {
  var players = $.grep(playersData, function(p) {return p.Position === position});
  var playersByPoints = players.sort(jsonUtil.sortByProperty('Points', true));
  var bestPlayersByPoints = playersByPoints.slice(0, byPoints);
  var playersByFactor = players.sort(jsonUtil.sortByProperty('Factor', true));
  var bestPlayersByFactor = playersByFactor.slice(0, byFactor);

  // players could be in both lists, make it unique
  var bestPlayers = jsonUtil.joinArrays(bestPlayersByPoints, bestPlayersByFactor, true, 'ID');

  // if not the nr wanted, add extra players
  // take theze by turn on points / on factor
  var cnt = $(bestPlayers).length;
  var nextByPoints = true;
  var cntExtra = 0;
  while (cnt < byPoints + byFactor) {
      cntExtra++;
      var isOK = false;
      while (!isOK) {
          var ix=0; // if the next player is already chosen, move to the next
          var extraPlayer = {};
          if (nextByPoints) {
              extraPlayer = playersByPoints[byPoints+cntExtra+ix-1];
          }
          else {
              extraPlayer = playersByFactor[byFactor+cntExtra+ix-1];
          }

          if (!bestPlayers.find(x => x.ID === extraPlayer.ID)) {
              bestPlayers.push(extraPlayer);
              isOK = true;
          }
          else {
              ix++;
          }
      }
      nextByPoints = !nextByPoints;
      cnt++;
  }
  bestPlayers = bestPlayers.sort(jsonUtil.sortByProperty('Points', true)); // add the end we want the players sorted by total points
  console.log('Best player for position ' + position);
  console.log(bestPlayersToString(bestPlayers));
  return bestPlayers;
}

function bestPlayersToString(bestPlayers) {
  var result = '';
  var sep = '';
  $(bestPlayers).each(function(ix, player) {
      result += sep;
      result += JSON.stringify(player);
      if (sep === '') sep = ',';
  });
  return result;
}

function setBestCombination(keepers, defenders, midfielders, attackers) {
  _bestCombination = [];
  $(keepers).each(function(ix, keeper){
      _bestCombination.push(keeper.ID);
  });
  $(defenders).each(function(ix, defender){
      _bestCombination.push(defender.ID);
  });
  $(midfielders).each(function(ix, midfielder){
      _bestCombination.push(midfielder.ID);
  });
  $(attackers).each(function(ix, attacker){
      _bestCombination.push(attacker.ID);
  });
}

/* Combinations */

var _combinations = [];
var _curCombination = [];
function determineCombinations(players, cnt) {
  _combinations = [];
  _curCombination = [];
  this.addCombinations(players, cnt);
}

/*
* Take 2 from 5 (keepers), 5 from 10 (defenders, attackera) or 6 from 12 (midfielders)
* It is a rather complex recursive method with nested iterations over the
* (remaining players).
*/
var _curLevel = 1;
function addCombinations(players, cnt) {
  for (var i=0; i<players.length; i++) {
      var player = players[i];
      _curCombination.push(player);
      if (_curCombination.length == cnt) {
          _combinations.push(_curCombination.slice(0)); // slicing creates a clone
          //console.log(curCombinationToString());
          _curCombination.pop();
          continue;
      }

      var curPlayers = players.slice(i+1);
      _curLevel++;
      addCombinations(curPlayers, cnt);
  };
  _curCombination.pop();
  _curLevel--;
}

function curCombinationToString() {
  var result = '';
  var sep = '';
  $(_curCombination).each(function(ix, player) {
      result += sep;
      result += JSON.stringify(player);
      if (sep === '') sep = ',';
  });
  return result;
}

任何想法將不勝感激!!!!

經過無數次嘗試,終於讓這個工作起來了。 當然,我想和你分享這個。

這是使其工作的本質:

  1. 循環必須在異步函數內
  2. 具有 1 個循環邏輯的代碼必須在 Promise 內
  3. 代碼也需要在 setTimeout 內
  4. 對此邏輯的調用必須使用 await 進行“注釋”

所以這有效:

 'use strict'; async function testPromise() { var msg; for (var ix=1; ix<10; ix++) { msg = 'Before loop #'+ix $('#log').html(msg); await doActualWork(); msg = 'After loop #'+ix $('#log').html(msg); } } /* * Perform logic for one loop */ function doActualWork() { return new Promise( function (resolve, reject) { window.setTimeout( function () { // do the actual work like calling an API resolve(); }, 100); } ); }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM