如何在Javascript中强制执行同步循环?

时间:2017-09-06 05:51:20

标签: javascript asynchronous synchronization indexeddb

我正在Google Chrome上编写在XAMPP上运行的本地应用程序。它与IndexedDB交互(我使用Jake Archibald的promise库)。这是我的问题。

让我们说我有一个具有2个属性的对象存储,日期和工资(那天赚钱)。我想console.log整个对象商店让我们说一个报告。以下是代码段:

//Report 4 days of work
for(Day = 1; Day <= 4; Day++) {
  dbPromise.then(function(db) {
    var tx = transaction("workdays", "readonly");
    return tx.objectStore("workdays").get(Day);
  }).then(function(val) {
    console.log(val.day + '\t' + val.salary + '$\n';
  })

我的期望是这样的:

1    100$
2    120$
3    90$
4    105$

但它实际上是错误的,并说&#34;无法读取 undefined &#34;的价值日。结果是循环没有等待dbPromise.then()...但是异步地继续,并且因为IndexDB请求很慢,所以当它们完成时,Day计数器已经是5并且没有匹配并返回记录。 / p>

我挣扎了一段时间然后找到了一个解决方法,在循环中放置一个DayTemp来捕捉这一天。

//Report 4 days of work
for(Day = 1; Day <= 4; Day++) {
  DayTemp = Day;
  dbPromise.then(function(db) {
    var tx = transaction("workdays", "readonly");
    return tx.objectStore("workdays").get(DayTemp);
  }).then(function(val) {
    console.log(val.day + '\t' + val.salary + '$\n';
  })

它运作良好。但那时它还没有。结果如下:

1    100$
4    105$
2    120$
3    90$

我需要它们。我需要做什么?非常感谢你!

注意:情况稍微复杂一些,所以我不能使用getAll()或游标等等。我真的要做循环。而且我也有兴趣在这个Javascript的同步/异步主题中受到启发。我是初学者。

更新:我想出来了!首先,我DateTemp的第二次尝试的结果实际上是这样的:

4    105$
4    105$
4    105$
4    105$

这基本上反映了一开始的同样问题。这次唯一的区别是Date被捕获,所以它没有超过4。 但我终于找到了所有这一切的解决方案。让我演示这个简单的Javascript片段:(1)

function dummy(day)
{
    dbPromise.then(function(db) {
      //Create transaction, chooose object stores, etc,..
      objectStore.get(Day);
    }).then(function(db) {
      console.log(day + '\t' + salary + '\$n');
    })
}
for(Day = 1; Day <= 4; Day++)
{
    dummy(Day);
}

如果我像上面那样编写代码,我将永远得到正确答案!

1    100$
2    120$
3    90$
4    105$

但是,如果我这样写:(2)

for(Day = 1; Day <= 4; Day++)
{
    dbPromise.then(function(db) {
      //Create transaction, chooose object stores, etc,..
      return objectStore.get(Day);
    }).then(function(val) {
      console.log(Day + '\t' + val.salary + '\$n');
    })
}

我总是会收到一个错误,因为在数据库请求完成创建事务时Day计数器已经是5,并输入使用get()计数器的Day方法!疯了吧?

似乎Javascript有一些东西来确定是否应该等待一个语句,这意味着它应该在其后面的其他语句开始执行之前完全执行。以(1)代码片段为例。在循环体内输入Day = 1的循环,Javascript看到dummy()作为参数传递Day,并决定&#34;我不会继续循环没有让dummy()先完成,否则他会把自己搞砸了#34;。我得到了一个漂亮的结果。

然而,在(2)代码段中。 Javascript进入循环,看到我打电话给then() dbPromise,它说&#34; Okk正在执行大男孩,但我没有看到任何理由我必须等你。你在Day内使用then()哈?我不在乎这件事,只看着对不起。在您提出要求时,我会增加Day!&#34;。事情变得混乱。

这是我的第一点。我的第二点是,我还发现indexedDB中的add()个请求是异步的,即使每个add()都在不同的事务中。但get()是同步的,所以很好。

现在希望您对我上面的更新答案提出意见。很确定他们在一些尴尬的方面是不正确的。必须有一些非常基本的东西,我在这里错过了Javascript。

1 个答案:

答案 0 :(得分:1)

我建议您了解以下主题:

  • 异步编程
  • Javascript函数提升
  • 定义函数和调用函数之间的区别

作为一般规则,永远不要在循环中定义函数。如果你没有在循环中定义一个函数,那么你将避免大部分的复杂性。

如果你坚持在循环中定义一个函数,你可以使用使用立即执行的函数表达式的技巧:

for(...) {
  (function defined_plus_call(a, b, c) {
    // operate on a and b and c here within this function's body
    // do NOT use the variables arg1ToUseForA, arg2ToUseForB, etc
  }(arg1ToUseForA, arg2ToUseForB, etc));
}

或者,如果您只为现代浏览器编写,并且已经熟悉promises和异步编程,那么您可以使用新的async / await语法,以便编写命令式循环:

function get_helper_function(tx, Day) {
  function executor(resolve, reject) {
    var request = tx.objectStore("workdays").get(Day);
    request.onsuccess = function() { resolve(request.result); };
    request.onerror = function() { reject(request.error); };
  }
  return new Promise(executor);     
}

async function foo(...) {
  for(Day = 1; Day <= 4; Day++) {
    var promise_result = await dbPromise;
    var tx = promise_result.transaction("workdays", "readonly");
    var val = await get_helper_function(tx, Day);
    console.log(val.day + '\t' + val.salary + '$\n';
  }
}

然后,如果你真的想写清洁代码,我会改变一些其他的东西。一,这些都是可以共享相同数据库连接和相同事务的基本读取。第二,你可以使用Promise.all来迭代几个承诺。

function get_by_day(tx, day) {
  function executor(resolve, reject) {
    var request = tx.objectStore("workdays").get(day);
    request.onsuccess = function() { resolve(request.result); };
    request.onerror = function() { reject(request.error); };
  }
  return new Promise(executor);     
}

function get_all(db) {
  var db = await dbPromise;
  var tx = db.transaction("workdays", "readonly");
  var promises = [];
  for(var day of days) {
    var promise = get_by_day(tx, day);
    promises.push(promise);
  }

  var all_promise = Promise.all(promises);
  return all_promise);
}

get_all().then(function(resolutions) {
  for(var val of resolutions) {
    console.log(val.day + '\t' + val.salary + '$\n';
  }
});

这样,get_all可以同时解析任何顺序的个人获取承诺,至少在理论上是这样。

然后,如果你想尝试优化,那就更进一步了,看看函数IDBObjectStore.prototype.getAll。而不是明确地获取每一天,在一个函数调用中加载一系列天。