代码之家  ›  专栏  ›  技术社区  ›  Gary

promise中promise数组的顺序。所有数据库事务的创建顺序?

  •  0
  • Gary  · 技术社区  · 6 年前

    在下面的代码中,

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

    承诺 entry_save save_state 都是 readwrite 数据库事务和 get_HTML readonly .两个 读写 事务可以组合在一起,但这会使维护的撤销/重做链变得复杂,并将两个事务的成功和回滚联系在一起,这是不希望的。

    这个 输入保存 事务需要在 拯救国家 交易搬家前 输入保存 进入 Promise.allSettled 这就是它的工作原理,因为 输入保存 交易是在其他交易之前创建的。这 MDN article 解释执行请求的顺序如何基于独立于请求的顺序创建事务的时间。

    我的问题是,每个promise的同步代码是否按照它在数组中的放置顺序进行处理,例如 输入保存 first始终会导致首先创建其事务,并保证首先执行其数据库请求?

    虽然它很有效,速度也很快,但我不想这样做:

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

    如果这很重要的话,那并不是它的写作方式,它更符合:

    entry_save().then( intermediate ); 哪里 intermediate 调用 许诺一切都解决了 .

    非常感谢。

    为了澄清一点,下面是上面引用的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.

    如果 输入保存 创造 trans1 拯救国家 创造 trans2 ,都在函数的同步代码中,这意味着不在 onsuccess onerror 数据库请求或类似事件的处理程序,MDN示例是否适用?

    因此,@jfriend00写道,

    函数按其在数组中的放置顺序调用, 但这只决定了异步操作的顺序 起动。

    由于事务是在异步代码开始之前在同步代码中创建的,因此这会按事务创建的时间顺序排列写请求的时间吗?

    我想测试一下,但我不确定如何测试。如果一次使用两个几乎相同的承诺 许诺一切都解决了 ,如何延迟第一个创建的事务的写入请求,使其发生在第二个创建的事务的写入请求之后,以测试它是否将首先写入?setTimeout应该终止事务。可能是在请求之前放置了一个长期运行的同步循环。


    这个问题最后的代码可以更准确地说明我试图问的问题。它以上面引用的文章中的MDN为例,将其扩展到一个文档中的两个承诺中 许诺一切都解决了 ,两者都试图从 成功 事件 get 要求

    问题是,在创建第二笔交易之前,第一笔交易的条款中的相同原则,无论请求的顺序如何,是否仍适用于此设置。由于承诺的同步部分将按照承诺在数组中的放置顺序进行处理,因此事务将在承诺中进行 p_1 将在 p_2 .然而 put 请求 成功 事件 收到 请进 p_1 由于循环生成一个大字符串而延迟。问题是威尔 p_1 还写过吗 p_2 ?

    在这个实验中,我无法得到 p_2 写之前 p_1 因此,MDN示例似乎适用于这种类型的设置。然而,我不能确定为什么,因为我不明白JS代码是如何真正解释/处理的。

    例如,为什么 req.onsuccess 在发出请求后,是否要定义功能?我问的 question 前一段时间,但仍然不知道这是否会影响我尝试在这里添加延迟的方式。我知道它不会反过来工作;但我的观点是,我不确定浏览器在发出put请求之前是如何处理同步循环的 p_1 要真正确定这个示例演示了MDN文章在这个设置中始终有效。然而,我可以观察到,随着循环迭代次数的增加,完成请求需要更长的时间;而且,在我观察到的所有情况下, p_1 以前总是写 p_2 .唯一的办法 p_2 以前写过 p_1 如果 p_1 根本不写,因为字符串占用了大量内存,导致事务在 p_1 被中止。

    话虽如此,回到我的问题的更完整的设置,关于一系列的承诺中的三个承诺 许诺一切都解决了 与要求相比 输入保存 在开始工作前完成 许诺一切都解决了 关于剩下的两个承诺,在我项目的完整代码中,由于我不确定的原因,后者比前者更快,也就是等待 输入保存 完成比将其包含在列表中更快 许诺一切都解决了 .

    我本来以为会是另一种情况。我现在能想到的唯一原因是 输入保存 拯救国家 都在写入同一个对象存储区,可能无论浏览器做什么,都相当于在第一个事务之前锁定对象存储区,这就是 输入保存 ,完成,移除锁所需的时间比需要的时间长 输入保存 在考试前完成 许诺一切都解决了 开始,但不涉及锁。我以为一切都会“提前”准备好,就等着这两个人 请求按交易顺序进行。它们按顺序进行,但比使用更慢或至少不如使用:

    输入保存()。然后(()=>{Promise.allSettled([save_state(),get_HTML()])。然后(…);
    

    而不是:

     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 );
              };
        }); }
    
    0 回复  |  直到 6 年前
        1
  •  1
  •   Josh    6 年前

    indexedDB将按照创建的顺序维护事务的顺序,除非这些事务不重叠(例如,在每个事务涉及的一组存储中不涉及同一个存储)。这几乎与你在更高的承诺层做什么无关。

    同时,依赖这种行为可能是不明智的,因为它是隐含的,有点令人困惑。所以,也许可以用承诺来线性化。只有当你需要最大的表现时,我怀疑这是否适用。

    此外,承诺在创建时就开始执行。它们不一定在那个时候结束,它们最终结束,而不是立即结束。这意味着调用的发生顺序与您“创建”封装indexedDB调用的承诺的顺序相同。这意味着它取决于创建事务的顺序。

    不管哪个承诺赢得比赛。不管使用承诺。全部的

    还有,我保证。所有人都将保持秩序,即使承诺完全违反秩序,仅供参考,但不要因此而放弃。

        2
  •  1
  •   jfriend00    6 年前

    当你这么做的时候:

    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() 只要立即向数据库发出一个写操作,那么它很可能会在之前向数据库写入 输入保存() 写入数据库。事实上,这两次数据库写入的顺序是不确定的和快速的。

    如果你需要 输入保存() 完成之前 save_state() ,那么以上根本不是编写代码的方法。你的代码并不能保证 输入保存() 在任何 save_state() 跑。

    相反,你应该做你似乎已经知道的事情:

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

    只有这样才能保证 输入保存() 将在 save_state() 开始跑步。而且,这假设你完全同意 save_state() get_HTML() 以不可预测的顺序同时运行。

    我的问题是,每个promise进程的同步代码是否按照它在数组中的放置顺序进行,这样,将条目_save放在第一位总是会导致首先创建它的事务,并保证首先执行它的数据库请求?

    这些函数是按照它们在数组中的放置顺序调用的,但这只决定异步函数的启动顺序。之后,它们都在同一时间运行,它们之间的内部计时取决于各自的异步操作需要多长时间,以及这些异步操作做什么。如果秩序很重要,你不能把他们都放在一场不确定的比赛中。这就是所谓的“比赛条件”。相反,您需要对代码进行结构化,以确保所需的操作先执行,后执行。