最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Promise in a loop! How to? - Stack Overflow

programmeradmin2浏览0评论

I've been trying to get this bit of code to work for a while, and I just can't seem to get it. It's for a live-updating graph, that takes in an array of [x, y] for each point, so an array of arrays.

The input is a JSON list ing from a server, that updates that same JSON object (so it changes one JSON and updates only the value):

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

I need to extract the value only.

I tried it like this:

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);
    };
};

Basically, getData() is trying to build an array of X = timestamp.now(), and Y = "Value" from JSON, then push that array into my "overall" data array.

Doing it this way makes value an array of [<timestamp>, Unresolved].

Doing it this way:

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

Makes valueArray be an actual [<timestamp>, <JSON "value"], if I console.log(value), except this way seems to hang the server forever and consume a huge amount of RAM for the tab, as if I was doing an infinite loop (hundreds of get requests to the web server, even though the max length should 100). I'm not sure of the what's going inside the Promise to get this behavior though.

This here is the code that works in the example:

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

    data.push(value);
};

It seems straightforward enough. Where it says y (in var value = [now += updateInterval, y];) make "y" get the value from my API.

I have no clue how to actually achieve this.

I'm following the example from this Flot example but I just can't manage to make it work with an actual live value from AJAX or Promise (I even tried fetch).

All examples for "live updating table" end up just using math.random(), which is pretty misleading, as it just kinda moves about, but isn't really live.

I believe I'm not resolving the promise in the loop properly, but due to lack of experience, at this point I'm not even sure what is wrong.

I'm not sure where in my code I would go Y = "live value", or whether I have to return result somewhere? I'm not familiar with Promises or AJAX that much.

I've been trying to get this bit of code to work for a while, and I just can't seem to get it. It's for a live-updating graph, that takes in an array of [x, y] for each point, so an array of arrays.

The input is a JSON list ing from a server, that updates that same JSON object (so it changes one JSON and updates only the value):

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

I need to extract the value only.

I tried it like this:

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);
    };
};

Basically, getData() is trying to build an array of X = timestamp.now(), and Y = "Value" from JSON, then push that array into my "overall" data array.

Doing it this way makes value an array of [<timestamp>, Unresolved].

Doing it this way:

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

Makes valueArray be an actual [<timestamp>, <JSON "value"], if I console.log(value), except this way seems to hang the server forever and consume a huge amount of RAM for the tab, as if I was doing an infinite loop (hundreds of get requests to the web server, even though the max length should 100). I'm not sure of the what's going inside the Promise to get this behavior though.

This here is the code that works in the example:

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

    data.push(value);
};

It seems straightforward enough. Where it says y (in var value = [now += updateInterval, y];) make "y" get the value from my API.

I have no clue how to actually achieve this.

I'm following the example from this Flot example but I just can't manage to make it work with an actual live value from AJAX or Promise (I even tried fetch).

All examples for "live updating table" end up just using math.random(), which is pretty misleading, as it just kinda moves about, but isn't really live.

I believe I'm not resolving the promise in the loop properly, but due to lack of experience, at this point I'm not even sure what is wrong.

I'm not sure where in my code I would go Y = "live value", or whether I have to return result somewhere? I'm not familiar with Promises or AJAX that much.

Share Improve this question edited May 3, 2018 at 14:08 Mormoran asked May 3, 2018 at 13:47 MormoranMormoran 79113 silver badges34 bronze badges 4
  • What's the error you're getting? Past a full debug please – Frondor Commented May 3, 2018 at 13:53
  • getValueData() is returning a constant so I believe you can't do getValueData().then(). – Satish Kumar Commented May 3, 2018 at 13:53
  • @Frondor there is no error, it just doesn't show anything as the "y" value of the arrays is just a Promise (unresolved) object, so the graphing library doesn't know what to do with it. I just don't get a graph at all (expected). If I try it with the method that hangs the server, I don't get any response, it just keeps sending get requests to the server infinitely. – Mormoran Commented May 3, 2018 at 14:11
  • Promises are functional and they don't get along with loops well. Promises already have some tools like Promise.all(), Promise.race() or dynamic sequential execution of promises to handle your needs on recurring asynchronous cases. If you are fond of loops then you should look into async-await abstraction. – Redu Commented May 3, 2018 at 20:09
Add a ment  | 

3 Answers 3

Reset to default 3

Promises in loops are rarely a good idea, you'll usually want to use Promise.all() to execute multiple Promises at once, and get their results collected in an array:

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 has some good materials on Promises: https://developer.mozilla/en-US/docs/Web/JavaScript/Guide/Using_promises

As a side note, executing 100 AJAX requests at the same time sounds pretty strenuous, even with Promise.all(). You should either try to optimize the backend if you have any influence over it, or look into Workers to execute the requests in batches.

You can generate array of Promises and then wait of resolve with 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);
  })

Some things you should take in mind

  • Your getValueData is not reaching the return statement because it resolves as soon as it reach Promise.resolve. (In the my example, now getData returns a promise)
  • Use Promise.all so you don't wait for the previous one to plete before executing the next... It resolves once every promise in the raid resolves, or one of them fails (you have to .catch that).
  • Learn more about Promises, they are scary at first, but you'll end up loving em

Try this code, it may not work because I don't have the full working example, but you can get around errors probably and get it to work.

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)
};
发布评论

评论列表(0)

  1. 暂无评论