Javascript:顺序,不是并行

Javascript: sequential, not parallel

我正在做一个项目,但无法让我的 javascript 按顺序工作。我知道 javascript 可以并行执行任务,因此当您向不响应的服务器发出请求时它不会卡住。这有它的优点和缺点。就我而言,这是一个痛苦的流浪汉。

场景如下,我使用 Firebase 来存储有多少人同意某个观点。这看起来如下:

我喜欢javascript
- 非常喜欢
- 我喜欢
- 喜欢或不喜欢它
- 不喜欢
- 我基本上讨厌它

人们现在可以 select 1 个选项。然后将他们的 selection 的记录放在选项下。结构:

-dfd21DeH67 : {
    Title : "I like javascript"
    options : {
        0 : {
            option : "I like it a lot"
            agrees : {
                -dfd21DQH6sq7 : "Jhon"
                -dfd21DQH6sq8 : "Luke"
                -dfd21DQH6sq9 : "Bruce"
            }
        }
        1 : {
            option : "I like it"
            agrees : {
                -dfd21DQH6sqA : "Harry"
                -dfd21DQH6sqB : "Jimmy"
            }
        }
        2 : {
            option : "like nor dislike it"
            agrees : {
                -dfd21DQH6sqC : "Timmy"
                -dfd21DQH6sqD : "Paul"
                -dfd21DQH6sqE : "Danny"
                -dfd21DQH6sqF : "Robin"
                -dfd21DQH6sqG : "Dan"
            }
        }
        3 : {
            option : "don't like it"
            agrees : {
                -dfd21DQH6sqH : "Nathan"
                -dfd21DQH6sqI : "Jerry"
            }
        }
        4 : {
            option : "I basically hate it"
            agrees : {
                -dfd21DQH6sqJ : "Tony"
            }
        }
    }
}

现在,我想查询一下。我需要的每个选项的结果是:
- 选项(例如 "I like it a lot")
- 同意计数(例如 n 人同意这个)
- 百分比(例如,13 人中有 3 人说:"I like javascript"。这将是 ~23%)

现在,获取 "option" 并在浏览器中显示:成功
获得 "agree count":成功感谢 numChildren()
得到 "percentage": 挑战,因为我需要他们的总和来计算这个。

关于如何实现这一点有什么想法吗?尝试使用一些回调技巧。不幸的是没有用。以下是我的愿景:

//javascript
var hash = "-dfd21DeH67";
var fbDB = new Firebase("https://<MyDB>.firebaseio.com/");
var buffer = [];
var sum;

fbDB.child(hash).once("value", function(opinion) {
    var thisOpinion = opinion.val();

    // First section
    $.each(thisOpinion.options, function(i) {
        fbPoll.child(hash + "/options/" + i + "/agrees").once("value", function(agrees) {
            votes.push(agrees.numChildren());
            sum += agrees.numChildren();
        });
    });

    // execute only if the First section is done
    // this is currently not the case, this gets executed before
    // the first one finishes => result: #ResultsPanel stays empty
    $.each(buffer, function(i) {
        $("#ResultsPanel").append(
            "<div class=\"OptionStatsBlock\">" +
            "Option: " + thisOpinion.options[i].option + "<br>" +
            "Total: " + buffer[i] + "<br>" +
            "Percentage: " + ((buffer[i] / sum) * 100) +
            "</div>"
        );
    });
});

提前致谢,

您可以使用 jQuery Promise 来处理调用的这种异步性质。 http://api.jquery.com/category/deferred-object/

甚至这个 post 也可能对您有所帮助 - http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/

对于其中一个,第二个 $.each 运行在空缓冲区数组上。所以它甚至不应该 运行。

更好的方法可能是向 "child_added" 事件添加事件侦听器,如下所示:从此处引用 https://www.firebase.com/docs/web/guide/retrieving-data.html#section-event-types

    var ref = new Firebase("https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts");
    // Retrieve new posts as they are added to Firebase
    ref.on("child_added", function(snapshot) {
      var newPost = snapshot.val();
      console.log("Author: " + newPost.author);
      console.log("Title: " + newPost.title);
    });

正如评论中所述:您似乎在与 Firebase(以及大多数现代网络)的异步特性作斗争。我鼓励您花尽可能多的时间来理解异步编程,以摆脱它目前造成的痛苦。它会让你在接下来的日子里远离更多的痛苦。

一种可能的解决方案

Firebase DataSnapshots 始终包含您请求的节点下的所有数据。因此,与其在循环中执行 once(这几乎总是一个坏主意),您可以只请求更高一级的数据,然后循环并使用 child:

// First section
$.each(thisOpinion.options, function(i) {
    var agrees = opinion.child("/options/" + i + "/agrees");
    votes.push(agrees.numChildren());
    sum += agrees.numChildren();
});

此代码同步执行。

请注意,我没有对此进行测试,因此可能存在错别字。它基本上只是对您的代码稍作修改,使用 DataSnapshot.child: https://www.firebase.com/docs/web/api/datasnapshot/child.html