Skip to content

Commit f30d5c1

Browse files
committed
[ns.Update] Перезапрос моделей в случае их невалидности #464
1 parent dd11096 commit f30d5c1

File tree

4 files changed

+86
-4
lines changed

4 files changed

+86
-4
lines changed

src/ns.update.js

+79-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242

4343
this.promise = new Vow.Promise();
4444

45+
/**
46+
* Количество перезапросов из-за невалидных моделей.
47+
* @type {number}
48+
* @private
49+
*/
50+
this._restartCount = 1;
51+
4552
options = options || {};
4653

4754
/**
@@ -85,6 +92,13 @@
8592
*/
8693
ns.Update.prototype._EVENTS_ORDER = ['ns-view-hide', 'ns-view-htmldestroy', 'ns-view-htmlinit', 'ns-view-async', 'ns-view-show', 'ns-view-touch'];
8794

95+
/**
96+
* Лимит на перезапрос моделей.
97+
* @type {number}
98+
* @constant
99+
*/
100+
ns.Update.prototype.RESTART_LIMIT = 2;
101+
88102
/**
89103
* Регистрирует указанное событие, добавляя к нему признаки ns.update
90104
* @private
@@ -153,7 +167,7 @@
153167
};
154168

155169
if (ns.Update.handleError(error, this)) {
156-
return [].concat(err.invalid, err.valid);
170+
return Vow.resolve([].concat(err.invalid, err.valid));
157171

158172
} else {
159173
return Vow.reject(error);
@@ -202,6 +216,12 @@
202216
this.log('collected incomplete views', views);
203217

204218
var models = views2models(views.sync);
219+
/**
220+
* Массив моделей, которые должны быть валидны перед отрисовкой.
221+
* @private
222+
*/
223+
this._models = models;
224+
205225
this.log('collected needed models', models);
206226
this.stopTimer('collectModels');
207227

@@ -340,11 +360,67 @@
340360
* потому что у видов будет уже другое состояние, если что-то поменяется между generateHTML и insertNodes
341361
* @private
342362
*/
343-
ns.Update.prototype._updateDOM = function() {
363+
ns.Update.prototype._startUpdateDOM = function() {
344364
if (this._expired()) {
345365
return this._rejectWithStatus(this.STATUS.EXPIRED);
346366
}
347367

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() {
348424
var html = this._renderUpdateTree();
349425
var node = ns.html2node(html || '');
350426
return this._insertNodes(node);
@@ -421,7 +497,7 @@
421497
// начинаем цепочку с промиса, чтобы ловить ошибки в том числе и из _requestAllModels
422498
Vow.invoke(this._requestAllModels.bind(this))
423499
.then(saveAsyncPromises, null, this)
424-
.then(this._updateDOM, null, this)
500+
.then(this._startUpdateDOM, null, this)
425501
.then(fulfillWithAsyncPromises, this._reject, this);
426502

427503
return this.promise;

test/spec/ns.update.edge-cases.js

+1
Original file line numberDiff line numberDiff line change
@@ -206,4 +206,5 @@ describe('ns.Update. Синтетические случаи', function() {
206206
expect(ns.request.models).to.have.callCount(0);
207207
});
208208
});
209+
209210
});

test/tests.yate

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ match .* ns-view-async-content {
2020

2121
match .* ns-view-error-content {
2222
"test ns-view-error-content"
23+
apply . ns-view-content
2324
}
2425

2526
// Это шаблон для проверки того, что yate сбилжен для тестов.

yate/noscript.yate

+5-1
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,8 @@ match .* ns-view-async-content {}
114114

115115
// Часть обязательных моделей не удалось получить или для них вернулась ошибка
116116
// @public
117-
match .* ns-view-error-content {}
117+
match .* ns-view-error-content {
118+
// Надо все равно отрисовать дерево,
119+
// чтобы не нарваться на ошибку "Can't find node for <view>"
120+
apply . ns-view-content
121+
}

0 commit comments

Comments
 (0)