#promise #rxjs #observable #dexie
Вопрос:
У меня есть угловое приложение, в котором есть хранилище DexieDb / IndexedDB для объектов, выбранных с карты. Я всегда очищаю базу данных перед добавлением новых элементов. Если новые элементы одинаковы, иногда может возникнуть ошибка ограничения:
selectionItemInfos.bulkaddd(): 1 из 1 операций не удалась. Ошибки: ConstraintError: Ключ уже существует в хранилище объектов.
Ошибка может быть воспроизведена путем повторного вызова метода ниже (в приложении как можно быстрее дважды щелкните элемент).:
public setItemInfos(itemInfos: IItemInfo[]): Observable<number> {
return from(this.db.selectionItemInfos.clear().then(() => {
return this.db.selectionItemInfos.bulkAdd(itemInfos);
}));
}
Однако, если я изменю реализацию на:
public setItemInfos(itemInfos: IItemInfo[]): Observable<number> {
const clear$ = from(this.db.selectionItemInfos.clear());
const bulkAdd$ = from(this.db.selectionItemInfos.bulkAdd(itemInfos));
return clear$.pipe(concatMap(() => bulkAdd$))
}
Я не смог воспроизвести его.
Интересно, есть ли что-то не так с 1-й реализацией / я ошибочно предположил, что таблица очищается при вызове bulkAdd ИЛИ что-то не так с реализацией DexieDb / IndexedDB (а именно, что она возвращается до того, как она действительно очищена)?
Однако мой реальный вопрос заключается в следующем: должны ли эти 2 метода быть эквивалентными? Т. е. мне просто повезло, что я еще не смог воспроизвести ошибку?
Комментарии:
1. Одно различие заключается в том, когда создаются Обещания. В первом примере вы создаете второй после разрешения первого. Во втором примере вы заранее создаете оба обещания, но я не знаю, может ли это быть причиной вашей проблемы.
2. вы имеете в виду Наблюдаемые, а не Обещания? В 1-м примере есть только 1 наблюдаемый, а во 2-м-2 наблюдаемых.
Ответ №1:
Во-первых, я удивлен, что второй пример не является тем, который приводит к ошибке.
const clear$ = from(this.db.selectionItemInfos.clear());
const bulkAdd$ = from(this.db.selectionItemInfos.bulkAdd(itemInfos));
return clear$.pipe(concatMap(() => bulkAdd$))
То, что вы завернули обещание в «от«, не делает его отложенным. Поэтому в этом случае clear() и bulkaddd() должны начать выполняться немедленно и асинхронно, до настройки потока.
Первая версия лучше, так как два вызова должны выполняться последовательно, но, конечно, могут завершиться неудачей, если они выполняются в быстрой последовательности. Это связано с тем, что у вас может быть два вызова clear() execute, а затем два вызова blukAdd (), поскольку вызовы setItemInfos (), вероятно, не будут ожидаться.
Таким образом, оба способа неверны, и единственная причина, по которой я мог догадаться, почему second менее подвержен ошибкам, заключается в том, что bulkAdd() действительно должен ждать, пока clear() не будет полностью выполнен, и проблема заключается в том, что у bulkaddd () просто два последовательных выполнения.
Способ заставить вашу первую реализацию работать без ошибок-использовать concactMap() или switchMap() для предотвращения нескольких одновременных исполнений. Разница между ними заключается в том, что последнее отменит предыдущие выполнения, когда следующий результат будет получен из потока более высокого уровня.
readonly setItemInfoSubject = new Subject<IItemInfo[]>();
private readonly doSetItemInfoSubcription = this.setItemInfoSubject.pipe(
switchMap(itemInfos => this.db.selectionItemInfos.clear()
.then(() = this.db.selectionItemInfos.bulkAdd(itemInfos)))
).subscribe();
Наконец, во втором примере не было особого смысла оборачивать второе обещание с помощью from, поскольку оно использовалось внутри оператора, который принимает ObservableInput. Псевдоним этого типа сопоставляется со следующими типами: Наблюдаемый, наблюдаемый, асинхронный, многообещающий, похожий на массив, Повторяющийся, читаемый поток.
Комментарии:
1. Ожидание обещания несколько раз просто дает вам обналиченную стоимость в обещании. Это когда-нибудь разрешится только один раз. Первый подход каждый раз порождает новое обещание, второй-нет. Это объясняет, почему второй не ошибается (но, скорее всего, он делает не то, что хочет автор).
2. setItemInfos(x).подписка вызывается каждый раз при вызове обработчика двойного клика. я думал, что обертывание обещания для наблюдаемого означает, что оно ведет себя как наблюдаемое
3. @charm Если вы вызовете функцию
this.db.selectionItemInfos.clear()
, то есть , она немедленно начнет ее выполнять — независимо от того, что делает функция, которой вы передаете результат. Ответ, как всегда, когда вы хотите отложить выполнение, заключается в том, чтобы вместо этого предоставить функцию. Проверьте отложенный , где вы можете завершить вызов функции, производящей обещание.4. В тебе есть это очарование. Я действительно отвык от привычки относиться к наблюдаемым как к обещаниям и возвращать новое. Это приводит меня в ситуации, подобные той, в которую ты попал. В вашем случае у вас действительно есть обещание, поэтому я не вижу смысла в создании наблюдаемого, ЕСЛИ вы не хотите воспользоваться некоторыми дополнительными функциями rxj, такими как concatMap или switchMap, и их способностью предотвращать одновременное выполнение.
5. Тогда не отказывайся от своих обещаний — все в порядке. Просто я так поступаю, и мне это нравится. Что вам нужно сделать, так это использовать карту переключения или карту объединения на более высоком уровне, если вы хотите, чтобы эти ошибки исчезли.