0

I understand that this is a basic question, but I can't figure it out myself, how to export my variable "X" (which is actually a JSON object) out of "for" cycle. I have tried a various ways, but in my case function return not the JSON.object itself, but a "promise.pending".

I guess that someone more expirienced with this will help me out. My code:

for (let i = 0; i < server.length; i++) {
    const fetch = require("node-fetch");
    const url = ''+(server[i].name)+'';
    const getData = async url => {
        try {
            const response = await fetch(url);
            return await response.json();
        } catch (error) {
        console.log(error);
        }
    };
    getData(url).then(function(result) { //promise.pending w/o .then
        let x = result; //here is real JSON that I want to export
    });
 }
 console.log(x); // -element is not exported :(
2
  • 3
    You are using a for loop which suggests you have more than one server you are trying to fetch from. But you have just one x. What should x be? an array? a concatenation of all the responses? Something else? Commented Jan 7, 2018 at 5:25
  • @Mark_M , you're absolutely right about "more then one server", so my "X" is array of { url & lastmodified } data from them, it's looked like this: [ { url: 'url.url', lastModified: 1515301164000 } ] ... [ { url: 'url.url', lastModified: 1515301164000 } ] Commented Jan 7, 2018 at 5:33

2 Answers 2

3

Here's some cleaner ES6 code you may wish to try:

const fetch = require("node-fetch");

Promise.all(
    server.map((srv) => {
        const url = String(srv.name);

        return fetch(url)
        .then((response) => response.json())
        .catch((err) => console.log(err));
    })
)
.then((results) => {
    console.log(results);
})
.catch((err) => {
    console.log('total failure!');
    console.log(err);
});

How does it work?

Using Array.map, it transforms the list of servers into a list of promises which are executed in parallel. Each promise does two things:

  • fetch the URL
  • extract JSON response

If either step fails, that one promise rejects, which will then cause the whole series to reject immediately.

Why do I think this is better than the accepted answer? In a word, it's cleaner. It doesn't mix explicit promises with async/await, which can make asynchronous logic muddier than necessary. It doesn't import the fetch library on every loop iteration. It converts the server URL to a string explicitly, rather than relying on implicit coercion. It doesn't create unnecessary variables, and it avoids the needless for loop.

Whether you accept it or not, I offer it up as another view on the same problem, solved in what I think is a maximally elegant and clear way.


Why is this so hard? Why is async work so counterintuitive?

Doing async work requires being comfortable with something known as "continuation passing style." An asynchronous task is, by definition, non-blocking -- program execution does not wait for the task to complete before moving to the next statement. But we often do async work because subsequent statements require data that is not yet available. Thus, we have the callback function, then the Promise, and now async/await. The first two solve the problem with a mechanism that allows you to provide "packages" of work to do once an asynchronous task is complete -- "continuations," where execution will resume once some condition obtains. There is absolutely no difference between a boring node-style callback function and the .then of a Promise: both accept functions, and both will execute those functions at specific times and with specific data. The key job of the callback function is to act as a receptacle for data about the asynchronous task.

This pattern complicates not only basic variable scoping, which was your main concern, but also the issue of how best to express complicated workflows, which are often a mix of blocking and non-blocking statements. If doing async work requires providing lots of "continuations" in the form of functions, then we know that doing this work will be a constant battle against the proliferation of a million little functions, a million things needing names that must be unique and clear. This is a problem that cannot be solved with a library. It requires adapting one's style to the changed terrain.

The less your feet touch the ground, the better. :)

Sign up to request clarification or add additional context in comments.

Comments

2

Javascript builds on the concept of promises. When you ask getData to to do its work, what is says is that, "OK, this is going to take some time, but I promise that I'll let you know after the work is done. So please have faith on my promise, I'll let you know once the work is complete", and it immediately gives you a promise to you.

That's what you see as promise.pending. It's pending because it is not completed yet. Now you should register a certain task (or function) with that promise for getData to call when he completes the work.

function doSomething(){
var promiseArray = [];
for (let i = 0; i < server.length; i++) {
    const fetch = require("node-fetch");
    const url = ''+(server[i].name)+'';
    const getData = async url => {
        try {
            const response = await fetch(url);
            return await response.json();
        } catch (error) {
        console.log(error);
        }
    };
    promiseArray.push(getData(url)); // keeping track of all promises
 }

 return Promise.all(promiseArray); //see, I'm not registering anything to promise, I'm passing it to the consumer

}

function successCallback(result) {
  console.log("It succeeded with " + result);
}

function failureCallback(error) {
  console.log("It failed with " + error);
}

let promise = doSomething(); // do something is the function that does all the logic in that for loop and getData
promise.then(successCallback, failureCallback);

3 Comments

Actually I solved problem with "promise.pending" myself in code above, but can't export the result out of function. btw, thank you so much for your answer, I'm trying it right now..
the funny thing is that I understand how your (and part of mine) code is works, but I'm still receiving "Cannot read property 'then' of undefined" in string with "promise.then(successCallback)" which is almost exact "logic" copy from my code.
I updated the code to take in to account that you need results from multiple servers. Used Promise.all

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.