|
42 | 42 |
|
43 | 43 | this.promise = new Vow.Promise();
|
44 | 44 |
|
| 45 | + /** |
| 46 | + * Количество перезапросов из-за невалидных моделей. |
| 47 | + * @type {number} |
| 48 | + * @private |
| 49 | + */ |
| 50 | + this._restartCount = 1; |
| 51 | + |
45 | 52 | options = options || {};
|
46 | 53 |
|
47 | 54 | /**
|
|
85 | 92 | */
|
86 | 93 | ns.Update.prototype._EVENTS_ORDER = ['ns-view-hide', 'ns-view-htmldestroy', 'ns-view-htmlinit', 'ns-view-async', 'ns-view-show', 'ns-view-touch'];
|
87 | 94 |
|
| 95 | + /** |
| 96 | + * Лимит на перезапрос моделей. |
| 97 | + * @type {number} |
| 98 | + * @constant |
| 99 | + */ |
| 100 | + ns.Update.prototype.RESTART_LIMIT = 2; |
| 101 | + |
88 | 102 | /**
|
89 | 103 | * Регистрирует указанное событие, добавляя к нему признаки ns.update
|
90 | 104 | * @private
|
|
153 | 167 | };
|
154 | 168 |
|
155 | 169 | if (ns.Update.handleError(error, this)) {
|
156 |
| - return [].concat(err.invalid, err.valid); |
| 170 | + return Vow.resolve([].concat(err.invalid, err.valid)); |
157 | 171 |
|
158 | 172 | } else {
|
159 | 173 | return Vow.reject(error);
|
|
202 | 216 | this.log('collected incomplete views', views);
|
203 | 217 |
|
204 | 218 | var models = views2models(views.sync);
|
| 219 | + /** |
| 220 | + * Массив моделей, которые должны быть валидны перед отрисовкой. |
| 221 | + * @private |
| 222 | + */ |
| 223 | + this._models = models; |
| 224 | + |
205 | 225 | this.log('collected needed models', models);
|
206 | 226 | this.stopTimer('collectModels');
|
207 | 227 |
|
|
340 | 360 | * потому что у видов будет уже другое состояние, если что-то поменяется между generateHTML и insertNodes
|
341 | 361 | * @private
|
342 | 362 | */
|
343 |
| - ns.Update.prototype._updateDOM = function() { |
| 363 | + ns.Update.prototype._startUpdateDOM = function() { |
344 | 364 | if (this._expired()) {
|
345 | 365 | return this._rejectWithStatus(this.STATUS.EXPIRED);
|
346 | 366 | }
|
347 | 367 |
|
| 368 | + // Проверяем валидность моделей перед стартом. |
| 369 | + // Из-за асинхронности модели могут оказаться невалидными. |
| 370 | + var models = { |
| 371 | + valid: [], |
| 372 | + invalid: [] |
| 373 | + }; |
| 374 | + var modelHasErrors = false; |
| 375 | + for (var i = 0, j = this._models.length; i < j; i++) { |
| 376 | + var model = this._models[i]; |
| 377 | + if (model.isValid()) { |
| 378 | + models.valid.push(model); |
| 379 | + |
| 380 | + } else { |
| 381 | + models.invalid.push(model); |
| 382 | + if (model.status === model.STATUS.ERROR) { |
| 383 | + modelHasErrors = true; |
| 384 | + break; |
| 385 | + } |
| 386 | + } |
| 387 | + } |
| 388 | + |
| 389 | + // если все хорошо, то переходим к обновлению DOM |
| 390 | + if (models.invalid.length === 0) { |
| 391 | + return this._updateDOM(); |
| 392 | + } |
| 393 | + |
| 394 | + // если в моделях есть ошибки или мы превысили лимит перезапусков, то фейлимся |
| 395 | + if (modelHasErrors || this._restartCount >= this.RESTART_LIMIT) { |
| 396 | + this.log('update has bad models', this._restartCount, models); |
| 397 | + // имитируем ошибку "Невалидные модели" |
| 398 | + var promise = this._onRequestModelsError(models); |
| 399 | + // делаем это синхронно |
| 400 | + if (promise.isFulfilled()) { |
| 401 | + ns.log.info('ns.Update.error-content', { |
| 402 | + _restartCount: this._restartCount |
| 403 | + }, models.invalid); |
| 404 | + // если ns.Update.handleError решит, что все ок, то запускаем обновление DOM |
| 405 | + return this._updateDOM(); |
| 406 | + |
| 407 | + } else { |
| 408 | + return promise; |
| 409 | + } |
| 410 | + |
| 411 | + } else { |
| 412 | + // если модели невалидные, но в них нет ошибок, то пробуем перезапуститься |
| 413 | + this._restartCount++; |
| 414 | + this.log('restart request models', this._restartCount); |
| 415 | + ns.log.info('ns.Update.restart', { |
| 416 | + _restartCount: this._restartCount |
| 417 | + }, models.invalid); |
| 418 | + return this._requestModels(this._models) |
| 419 | + .then(this._startUpdateDOM, null, this); |
| 420 | + } |
| 421 | + }; |
| 422 | + |
| 423 | + ns.Update.prototype._updateDOM = function() { |
348 | 424 | var html = this._renderUpdateTree();
|
349 | 425 | var node = ns.html2node(html || '');
|
350 | 426 | return this._insertNodes(node);
|
|
421 | 497 | // начинаем цепочку с промиса, чтобы ловить ошибки в том числе и из _requestAllModels
|
422 | 498 | Vow.invoke(this._requestAllModels.bind(this))
|
423 | 499 | .then(saveAsyncPromises, null, this)
|
424 |
| - .then(this._updateDOM, null, this) |
| 500 | + .then(this._startUpdateDOM, null, this) |
425 | 501 | .then(fulfillWithAsyncPromises, this._reject, this);
|
426 | 502 |
|
427 | 503 | return this.promise;
|
|
0 commit comments