Порядок массива обещаний в Promise.allSettled и порядок, в котором создаются транзакции базы данных? - PullRequest
2 голосов
/ 19 февраля 2020

В следующем коде

Promise.allSettled( [ entry_save(), save_state(), get_HTML() ] ).then( ... );

обещания entry_save и save_state являются readwrite транзакциями базы данных, а get_HTML - readonly. Две readwrite транзакции могут быть объединены вместе, но это усложняет поддерживаемую цепочку отмены / повтора и связывает успех и откат двух вместе, что нежелательно.

Транзакции entry_save необходимо записать до транзакции save_state. Перед перемещением entry_save в Promise.allSettled так оно и было, потому что транзакция entry_save была создана раньше, чем транзакции других. Эта статья MDN объясняет, как порядок, в котором выполняются запросы, основан на том, когда транзакции создаются независимо от порядка, в котором выполняются запросы.

Мой вопрос заключается в том, выполняет ли синхронный код каждого процесса обещания в том порядке, в котором он размещен в массиве, так что размещение entry_save первым всегда приведет к тому, что его транзакция будет создана первой, а гарантии его запросов к базе данных будут выполнены первыми?

Хотя это работает и достаточно быстро, я бы предпочел не делать этого:

entry_save().then( () => { Promise.allSettled( [ save_state(), get_HTML() ] ) } ).then( ... );

Если это имеет значение, это не совсем то, как написано, это больше соответствует:

entry_save().then( intermediate ); где intermediate вызывает Promise.allSettled.

Спасибо.

Для пояснения ниже приведен пример, приведенный в цитированном выше документе MDN.

var trans1 = db.transaction("foo", "readwrite");
var trans2 = db.transaction("foo", "readwrite");
var objectStore2 = trans2.objectStore("foo")
var objectStore1 = trans1.objectStore("foo")
objectStore2.put("2", "key");
objectStore1.put("1", "key");

After the code is executed the object store should contain the value "2", since trans2 should run after trans1.

Если entry_save создает trans1 и save_state создает trans2, и все в синхронном коде функций, то есть не в пределах onsuccess или onerror ха После запроса к базе данных или чего-то подобного пример MDN не будет храниться?

Таким образом, где @ jfriend00 пишет,

Функции вызываются в порядке их размещения в массив, но это только определяет порядок, в котором запускаются асинхронные.

будет этот порядок синхронизации запросов на запись по сравнению с созданием транзакций, так как транзакции создаются в синхронном код до того, как может начаться асинхронный?

Я хотел бы проверить его, но я не уверен, как. Если в Promise.allSettled используются два почти идентичных обещания, как можно отложить запрос на запись первой созданной транзакции, чтобы он выполнялся после запроса на запись второй созданной транзакции, чтобы проверить, будет ли он записан первым? SetTimeout должен завершить транзакцию. Возможно, длительный синхронный l oop, помещенный перед запросом.


Код в самом конце этого вопроса может лучше проиллюстрировать то, что я пытался задать. Он берет пример MDN в приведенной выше статье и распределяет его по двум обещаниям, помещенным в Promise.allSettled, оба из которых пытаются выполнить запись в одно и то же хранилище объектов из события onsuccess запроса get.

Вопрос заключался в том же принципе, что и в статье первой созданной транзакции, написанной до создания второй транзакции, независимо от порядка выполнения запросов, все еще сохраняющегося в этой конфигурации. Поскольку синхронные части обещаний будут обрабатываться в порядке размещения обещаний в массиве, транзакция в обещании p_1 будет создана до операции p_2. Однако запрос put в событии onsuccess запроса get в p_1 задерживается из-за того, что l oop создает большую строку. Вопрос в том, будет ли p_1 писать еще до p_2?

Экспериментируя с этим, я не могу заставить p_2 писать до p_1. Таким образом, кажется, что пример MDN применяется даже в этом типе установки. Однако я не могу быть уверен, почему, потому что я не понимаю, как действительно интерпретируется / обрабатывается код JS.

Например, почему можно определить функцию req.onsuccess после выполнения запроса? Я задал этот вопрос иногда go, но все еще не знаю достаточно, чтобы быть уверенным, что это не повлияет на то, как я пытался добавить задержку здесь. Я знаю, что это не сработает; но я хочу сказать, что я не уверен, как браузер обрабатывает эту синхронную l oop до того, как запрос put сделан в p_1, чтобы действительно точно знать, что этот пример демонстрирует, что статья MDN ВСЕГДА выполняется в этой конфигурации. Тем не менее, я могу заметить, что для выполнения запросов требуется больше времени, поскольку увеличивается число итераций l oop; и во всех случаях, которые я наблюдал, p_1 всегда пишет до p_2. Единственный способ p_2 писать до p_1 - это если p_1 вообще не пишет из-за того, что строка занимает много памяти, что приводит к прерыванию транзакции в p_1.

сказал, и возвращаясь к более полной постановке моего вопроса относительно трех обещаний в массиве Promise.allSettled по сравнению с требованием entry_save завершить до начала Promise.allSettled на двух оставшихся обещаниях, в полном коде моего проекта по причинам, в которых я не уверен, последнее выполняется быстрее первого, то есть ожидание завершения entry_save происходит быстрее, чем включение его в Promise.allSettled.

Я ожидал, что все будет наоборот. Единственная причина, о которой я могу подумать на этом этапе, заключается в том, что, поскольку entry_save и save_state записывают в одно и то же хранилище объектов, возможно, что бы браузер ни делал, эквивалентно блокировке хранилища объектов до первой транзакции, то есть entry_save, для завершения и снятия блокировки требуется больше времени, чем для завершения entry_save до начала Promise.allSettled без блокировки. Я думал, что все будет готово "заранее", просто ожидая двух put запросов в порядке транзакции. Они проходили по порядку, но медленнее или, по крайней мере, не так быстро, как при использовании:

entry_save().then( () => { Promise.allSettled( [ save_state(), get_HTML() ] ) } ).then( ... );

вместо:

 Promise.allSettled( [ entry_save(), save_state(), get_HTML() ] ).then( ... );

function p_all() { Promise.allSettled( [ p_1(), p_2() ] ); }

function p_1()
  {
    return new Promise( ( resolve, reject ) =>
      {
        let T = DB.transaction( [ 'os_1', 'os_2' ], 'readwrite' ),
            q = T.objectStore( 'os_1' ),
            u = T.objectStore( 'os_2' ),
            req, i, t ='', x = '';    

     req = q.get( 1 );

     req.onsuccess = () =>
       {
         let i, t, r = req.result;
         for ( i = 1; i < 10000000; i++ ) t = t + 'This is a string';
         r.n = 'p1';
         u.put( r );
         console.log( r );
       };
    }); }

function p_2()
  {
    return new Promise( ( resolve, reject ) =>
      {
       let T = DB.transaction( [ 'os_1', 'os_2' ], 'readwrite' ),
           q = T.objectStore( 'os_1' ),
           u = T.objectStore( 'os_2' ),
           req;    

        req = q.get( 1 );

        req.onsuccess = () =>
          {
            let r = req.result;
            r.n = 'p2';
            u.put( r );
            console.log( r );
          };
    }); }

Ответы [ 2 ]

1 голос
/ 20 февраля 2020

indexedDB будет поддерживать порядок транзакций в созданном порядке, за исключением случаев, когда эти транзакции не пересекаются (например, не включают одно и то же хранилище из набора хранилищ, в которое входит каждое из них). это в значительной степени независимо от того, что вы делаете на более высоком уровне обещаний.

в то же время, возможно, неразумно полагаться на такое поведение, потому что оно неявное и немного запутанное. так что, возможно, можно линеаризовать обещания. единственное преимущество - когда вам нужна максимальная производительность, что, я сомневаюсь, применимо.

Более того, обещания начинают выполняться в момент их создания. они просто не обязательно заканчиваются в это время, они заканчиваются в конце концов, а не сразу. это означает, что вызовы происходят в том порядке, в котором вы «создаете» обещания, заключающие в себе вызовы indexedDB. Это означает, что он зависит от порядка, в котором вы создаете транзакции.

независимо от того, какое обещание выиграет гонку. Кроме того, независимо от использования обещания. 1011 *

, обещание будет сохранять порядок даже в том случае, если обещания выполнены не по порядку, просто к вашему сведению, но не позволяйте этому сбить вас с толку.

1 голос
/ 19 февраля 2020

Когда вы делаете это:

Promise.allSettled( [ entry_save(), save_state(), get_HTML() ] ).then(...)

Это эквивалентно этому:

const p1 = entry_save();
const p2 = save_state();
const p3 = get_HTML();

Promise.allSettled([p1, p2, p3]).then(...);

Итак, отдельные вызовы функций, которые вы запускаете, такие как save_state(), запускаются в указанном порядке , Но каждый из этих вызовов асинхронный, поэтому внутренний порядок того, что происходит до того, как что-то еще, действительно зависит от того, что они делают внутри, поскольку все они могут быть в полете одновременно, и части их выполнения могут чередоваться в неопределенном порядке.

Представьте, что entry_save() на самом деле состоит из нескольких асинхронных фрагментов, таких как сначала чтение некоторых данных с диска, затем изменение данных, а затем запись их в базу данных. Это вызвало бы первую асинхронную операцию, чтобы прочитать некоторые данные с диска и затем немедленно вернуть обещание. Тогда save_state() получит начать выполнение. Если save_state() просто немедленно произвел запись в базу данных, то он вполне может записать в базу данных до того, как entry_save() сделает запись в базу данных. На самом деле последовательность двух операций записи в базу данных является неопределенной и быстрой.

Если вам нужно entry_save() завершить до save_state(), то вышеприведенное НЕ является способом вообще его кодировать. Ваш код не гарантирует, что все действия entry_save() будут выполнены до запуска любого из save_state().

Вместо этого вы ДОЛЖНЫ делать то, что, как вам кажется, уже известно:

entry_save().then( () => { Promise.allSettled( [ save_state(), get_HTML() ] ) } ).then( ... );

Только это гарантирует, что entry_save() завершится до запуска save_state(). И это предполагает, что вы в порядке с save_state() и get_HTML(), работающими одновременно и в непредсказуемом порядке.

Мой вопрос заключается в том, выполняет ли синхронный код каждого процесса обещания в следующем порядке: который он помещает в массив, так что при первом размещении entry_save всегда будет сначала создаваться его транзакция и гарантируется, что ее запросы к базе данных будут выполняться первыми?

Функции вызываются в том порядке, в котором они находятся помещается в массив, но это только определяет порядок, в котором запускаются асинхронные. После этого все они находятся в полете одновременно, и внутренняя синхронизация между ними зависит от того, сколько времени занимают их отдельные асинхронные операции и что делают эти асинхронные операции. Если порядок имеет значение, вы не можете просто поставить их всех в неопределенную гонку. Это называется «состояние гонки». Вместо этого вам нужно будет структурировать свой код, чтобы гарантировать, что желаемая операция выполняется в первую очередь перед теми, которые должны быть выполнены после нее.

...