循环承诺!如何?

Promise in a loop! How to?

一段时间以来,我一直在尝试让这段代码工作,但我似乎就是做不到。它用于实时更新图,每个点都包含一个 [x, y] 数组,因此是一个数组数组。

输入是来自服务器的 JSON 列表,它更新同一个 JSON 对象(因此它更改一个 JSON 并仅更新值):

[
  {
    "Value": 10,
    "Name": "Name of variable"
  }
]

我只需要提取值。

我这样试过:

var getValueData = async function() {
    var valueJSON = await Promise.resolve($.getJSON( "{% url 'get_value' %}" ));
    return valueJSON[0]['Value'];
};

var data = [];
var totalPoints = 100;
var updateInterval = 1000;
var now = new Date().getTime();

function getData() {
    data.shift();

    while (data.length < totalPoints) {
        var value = [now += updateInterval,
                    getValueData().then(function(result) {
                        result;
                    })
                   ];

        data.push(value);
    };
};

基本上,getData() 试图从 JSON 构建一个 X = timestamp.now() 和 Y = "Value" 的数组,然后将该数组推入我的 "overall"数据数组。

这样做会使 value 成为 [<timestamp>, Unresolved].

的数组

这样做:

while (data.length < totalPoints) {
    getZScoreData().then(function (result) {
        var valueArray = [now += updateInterval, result];
        data.push(valueArray);
    });
};

使 valueArray 成为实际的 [<timestamp>, <JSON "value"],如果我 console.log(value),除了这种方式似乎永远挂起服务器并为选项卡消耗大量 RAM,就好像我正在做一个无限循环(数百个对 Web 服务器的获取请求,即使最大长度应该是 100)。不过,我不确定 Promise 内部发生了什么以获得这种行为。

这是在示例中运行的代码:

while (data.length < totalPoints) {     
    var y = Math.random() * 100;
    var value = [now += updateInterval, y];

    data.push(value);
};

看起来很简单。它说 y (in var value = [now += updateInterval, y];) make "y" get the value from my API.

我不知道如何真正做到这一点。

我正在按照 this Flot example 中的示例进行操作,但我无法使其与 AJAX 或 Promise 中的实际实时值一起使用(我什至尝试过获取)。

"live updating table" 的所有示例最终都只使用了 math.random(),这是非常具有误导性的,因为它只是有点移动,但并不是真的存在。

我相信我没有正确解决循环中的承诺,但由于缺乏经验,此时我什至不确定哪里出了问题。

我不确定在我的代码中我会去哪里 Y = "live value",或者我是否必须 return 在某个地方产生结果?我不太熟悉 Promises 或 AJAX。

循环中的 Promise 很少是个好主意,您通常会希望使用 Promise.all() 一次执行多个 Promise,并将它们的结果收集在一个数组中:

function getData() {
    // Note: Why is this here?
    data.shift();

    var promises = [];
    while (data.length < totalPoints) {
        promises.push(getValueData());
    }

    Promise.all(promises).then(results => {
        for (let result of results) {
            var value = [
                now += updateInterval,
                result
            ];
            data.push(value);
        }
    });
};

MDN 有一些关于 Promises 的好材料:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

附带说明一下,即使使用 Promise.all(),同时执行 100 个 AJAX 请求听起来也很吃力。如果您对后端有任何影响,您应该尝试优化后端,或者查看 Workers 以批量执行请求。

您可以生成 Promise 数组,然后使用 Promise.all 等待解决。

// Let's imitate some async operation
// It is instead of your getValueData
function twiceAsync(a) {
  // returns a promise resolving to twice argument
  return new Promise(function(ok) {
    setTimeout(function(){ok(2*a)}, 100);
  });
}

var pList = [];
for (var i = 0; i < 100; ++i) {
  // collect promises returned by twiceAsync to an array
  pList.push(twiceAsync(i));
}

Promise.all(pList) // provide array of promises to Promise.all
  .then(function(results) { // receive resolved results, they should be even numbers from 0*2 to 99*2
    console.log(results);
  })

你应该记住的一些事情

  • 您的 getValueData 没有达到 return 声明,因为它 到达 Promise.resolve 后立即解析。 (在我的例子中, 现在 getData return 是一个承诺)
  • 使用Promise.all这样你就不用等了 在执行下一个之前完成前一个......它 一旦突袭中的每一个承诺都解决了,或者其中之一解决了 失败(你必须.catch那个)。
  • 详细了解 Promises, they are scary at first, but you'll end up loving em

试试这个代码,它可能无法工作,因为我没有完整的工作示例,但你可能会绕过错误并让它工作。

var results = [],
    totalPoints = 100,
    pointsLeft = totalPoints, // since we are using Promise.all, now we have to track this instead of results.length
    updateInterval = 1000,
    promises = [],
    getValueData = async function(timestamp) {
        await $.getJSON('yourURL').done(function (data) {
           pointsLeft-- // promise fulfilled 
           Promise.resolve({ data, timestamp })
        }).fail(Promise.reject)
    };

function getData() {
    // results.shift(); why?

    while (pointsLeft) {
        // push the promise to the stack, collect them later
        promises.push(getValueData(Date.now()))
    };


    Promise.all(promises).then(function (responses) {
        responses.forEach(function (data, timestamp) {
            results.push([timestamp, data])
        })
    }).catch(console.warn)
};