在处理 Moment JS 时遇到问题 (Angular)

Having trouble dealing with Moment JS (Angular)

我在使用 Moment JS 时遇到问题。基本上,我有一些广播电台的元数据,在我的 php 调用中,我进入 return 歌曲的 'duration',歌曲开始时的 'timestamp'。

我用Moment JS做了一些计算,得到歌曲结束的时间,然后我发现了不同。但是,不同之处在于 return 是一个负数,这会破坏应用程序。

如果有人能帮助我,那就太好了。

这是我的笑话http://plnkr.co/edit/joVLYdTKY5dZBOTNfTOI

服务

angular.module('starter', [])

.run(function(CurrentTrack){
   CurrentTrack.refreshTrackData();
})

.controller('radioCtrl', function($scope,CurrentTrack) {  
   console.log(CurrentTrack);
   $scope.CurrentTrack = CurrentTrack;  
})

.service('CurrentTrack',function(radioData,$timeout){

   var currentTrack = this;

   this.setTrackData = function(trackData){

      currentTrack.coverUrl = trackData.cover_url;
      currentTrack.title = trackData.title;
      currentTrack.artist = trackData.artist;
      currentTrack.duration = moment.duration(parseInt(trackData.duration));
      currentTrack.startedAt = moment.unix(trackData.timestamp);
      currentTrack.finishesAt = moment(this.startedAt.add(this.duration));
      currentTrack.updateIn = this.finishesAt.diff(moment());

      currentTrack.refreshing = false;
      return currentTrack.updateIn;
   }

   this.refreshTrackData = function(){
      currentTrack.refreshing = true;
      return radioData.refresh()
         .then(currentTrack.setTrackData.bind(currentTrack))
         .then(currentTrack.scheduleUpdate);
   }

   this.scheduleUpdate = function(ms){
      console.log(ms)
      $timeout(function(){
         currentTrack.refreshTrackData()
      },ms);
      return;
   }  
})

工厂

.factory('radioData', function($http,$timeout) {
   var retries = 0;

   function parseResponse(response){
      retries = 0;
      if(!response.data.results){
         console.log('no results')
         return false;
      }
      console.log('refreshed...')
      return response.data.results[0];
   }

   function makeRequest(){
      console.log('refreshing...')
      return $http.get('http://radio-sante-animale.fr/blah11.php?  callback=jsonpCallback')
   }

   function retry(errResponse){
      console.error('timed out');
      //wait for a sec
      retries++;
      if(retries > 5){
         throw new Error('timed out after 5 attempts!');
      }
      //oops
      return $timeout(makeRequest,1000).then(null,retry);
   }

   var radioData = {
      refresh: function() {
         return makeRequest()
           .then(null,retry)
           .then(parseResponse)
           .catch(function(err){
               console.log(err);
           });
      }
   };
   return radioData;
});

来自http://momentjs.com/docs/#/displaying/difference/

If the moment is earlier than the moment you are passing to moment.fn.diff, the return value will be negative.

您需要反转 diff 中的日期。

编辑:像这样 -

currentTrack.updateIn = moment().diff(this.finishesAt);

首先我改变了:

currentTrack.finishesAt = moment(this.startedAt.add(this.duration));

至:

currentTrack.finishesAt = moment(currentTrack.startedAt).add(currentTrack.duration);

在原始代码中,您正在改变 startedAt 时间然后进行克隆。我还将所有 this 更改为 currentTrack 以使其更加一致。

问题

当服务器保持的时间和客户端保持的时间不同时,错误就会出现(不是因为你有任何颠倒)。

基本上,服务器会向您发送 trackData.timestamp,您可以将其解析并转换为片刻以用作您的 startedAt 时间。然后将 trackData.duration 更改为 moment.duration 并将其添加到 startedAt 时间以获得 finishesAt 时间。

如果客户端保持的时间比服务器时间早运行,则计算出的finishesAt时间将早于歌曲实际结束的时间。一个夸张的例子使这一点更清楚。

var app = angular.module('app', []);

app.controller('myController', function($scope, $timeout, $interval, Server) {
  function updateTime() {
    $scope.serverTime = moment().subtract(1,'s');
    $scope.clientTime = moment();  
  }
  
  function refreshInfo() {
    Server.getSongInfo().then(parseInfo).then(scheduleRefresh);
  };
  
  function parseInfo(trackData) {
    $scope.songInfo = trackData;
    var startedAt = trackData.timestamp;
    $scope.finishesAt = moment(startedAt).add(trackData.duration, 'ms');
    $scope.updateIn = $scope.finishesAt.diff(moment());
    return $scope.updateIn;
  }
  
  function scheduleRefresh(updateIn) {
    if (updateIn < 0) {
      $scope.bugged = true;
    } else {
      $scope.bugged = false;
    }
    $timeout(refreshInfo, updateIn);
  }
  
  $scope.songInfo = "loading";
  updateTime();
  refreshInfo();
  
  $interval(updateTime, 1000);
});

app.service('Server', function($timeout, $interval) {
  var song = {
    duration: 5000
  };
  
  this.getSongInfo = function() {
    return $timeout(function() { return this.trackData });
  };
  
  function nextSong() {  
    this.trackData = {
      duration: song.duration,
      timestamp: moment().subtract(1,'s')
    };
  }
  
  nextSong();
  
  $interval(nextSong, song.duration);
});
.container {
  display: flex;
  flex-flow: row wrap;
}
.row {  
  display: flex;
  flex-direction: row;
  flex-flow: space-around;
  width: 100%;
}
.col {
  margin: auto;
  text-align: center;
}
.red {
  background-color: red;
}
<head>
  <script data-require="angular.js@1.4.0-beta.3" data-semver="1.4.0-beta.3" src="https://code.angularjs.org/1.4.0-beta.3/angular.js"></script>
  <script data-require="moment.js@2.8.3" data-semver="2.8.3" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.min.js"></script>
</head>

<div ng-app='app' ng-controller='myController'>
  <div class="container">
    <div class='row'>
      <div class='col' ng-class='{red:bugged}'>
        <h3>Song Info</h3>
        <div>{{ songInfo }}</div>
      </div>
    </div>
    <div class="row">
      <div class='col'>
        <h3>Server</h3>
        <div>{{ serverTime.format("hh:mm:ss") }}</div>
      </div>
      <div class='col'>
        <h3>Finishes At</h3>
        <div>{{ finishesAt.format("hh:mm:ss") }}</div>
      </div>   
      <div class='col'>
        <h3>Update In</h3>
        <div>{{ updateIn }}</div>
      </div>  
      <div class='col'>
        <h3>Client</h3>
        <div>{{ clientTime.format("hh:mm:ss") }}</div>
      </div>
    </div>
  </div>
</div>

Whenever the song info div is red, the client is in the loop where it is requesting new track info and parsing out a finish time that is earlier than the current moment, which immediately makes it request new track info.

angular.module('starter', [])

.run(function(CurrentTrack) {
  CurrentTrack.refreshTrackData();
})

.controller('radioCtrl', function($scope, CurrentTrack) {
  $scope.CurrentTrack = CurrentTrack;

})

.service('CurrentTrack', function(radioData, $timeout) {

  var currentTrack = this;

  this.setTrackData = function(trackData) {
    currentTrack.coverUrl = trackData.cover_url;
    currentTrack.title = trackData.title;
    currentTrack.artist = trackData.artist;
    currentTrack.duration = moment.duration(parseInt(trackData.duration));
    currentTrack.startedAt = moment.unix(trackData.timestamp);
    currentTrack.finishesAt = moment(currentTrack.startedAt).add(currentTrack.duration);
    currentTrack.updateIn = currentTrack.finishesAt.diff(moment())
    
    console.log(currentTrack, trackData);
    currentTrack.refreshing = false;
    return currentTrack.updateIn;
  }

  this.refreshTrackData = function() {
    currentTrack.refreshing = true;
    return radioData.refresh()
      .then(currentTrack.setTrackData)
      .then(currentTrack.scheduleUpdate);
  }

  this.scheduleUpdate = function(ms) {
    console.log("update in " + ms)
    $timeout(function() {
      currentTrack.refreshTrackData()
    }, ms);
    return;
  }
})


.factory('radioData', function($http, $timeout) {

  var retries = 0;

  function parseResponse(response) {
    retries = 0;
    if (!response.data.results) {
      console.log('no results')
      return false;
    }
    console.log('response parsed.')
    return response.data.results[0];
  }

  function makeRequest() {
    console.log('making request.')
    return $http.get('http://radio-sante-animale.fr/blah11.php?callback=jsonpCallback')
  }

  function retry(errResponse) {
    console.error('timed out');
    //wait for a sec
    retries++;
    if (retries > 5) {
      throw new Error('timed out after 5 attempts!');
    }
    //oops
    return $timeout(makeRequest, 1000).then(parseResponse, retry);
  }

  var radioData = {
    refresh: function() {
      return makeRequest()
        .then(parseResponse, retry)
        .catch(function(err) {
          console.log(err);
        });
    }
  };
  return radioData;
});
<!DOCTYPE html>
<html ng-app="starter">

<head>
  <script data-require="angular.js@1.4.0-beta.3" data-semver="1.4.0-beta.3" src="https://code.angularjs.org/1.4.0-beta.3/angular.js"></script>
  <script data-require="moment.js@2.8.3" data-semver="2.8.3" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.3/moment.min.js"></script>
</head>

<body ng-controller="radioCtrl">
  <h3>Current Music Information</h3>
  <p ng-show="CurrentTrack.refreshing">refreshing...</p>

  <img style="width:300px;height:300px;" src="{{CurrentTrack.coverUrl}}" alt="">
  <p>
    Title : {{ CurrentTrack.title }}
    <br />Artist : {{ CurrentTrack.artist }}
    <br />The song started at {{ CurrentTrack.startedAt }}
    <br />Duration of the song {{ CurrentTrack.duration.asSeconds() }} seconds
    <br />Finishes At {{ CurrentTrack.finishesAt }}
    <br />Update In {{ CurrentTrack.updateIn }}
    <br />
</body>

</html>

实际上我不确定解决此问题的最佳方法,但希望有人有更好的答案。

一个 hack 解决方案是为 finishesAt 添加更多时间(一些可接受的错误量)以留出一点回旋余地。

currentTrack.finishesAt = moment(currentTrack.startedAt).add(currentTrack.duration).add(1, 's');

在我这边,它通过在你的 $timeout 方法

中添加 Math.abs 来工作
this.scheduleUpdate = function(ms) {
    console.log( 'update in :' + ms)
    $timeout(function() {
        currentTrack.refreshTrackData()
    }, Math.abs(ms));
    return;
}