-2

I want to fetch data and have it ready for another function to use as a javaScript object. The problem is that the data is fetched after the program completes. Here is the link to the project: https://github.com/bigbassroller/isomorphic-js/blob/master/src/components/pages/Home/HomeController.js. See code here:

import "babel-polyfill";
import Controller from '../../../lib/controller';
import nunjucks from 'nunjucks';
import fetch from "isomorphic-fetch";
import promise from "es6-promise";


function onClick(e) {
  console.log(e.currentTarget);
}

function getData(context) {
 let data = {
  "name": "Leanne Graham"
 }
 return data;
}

function fetchData(context) {
    return fetch("http://jsonplaceholder.typicode.com/users/1").then(function(response) {
       let data = response.json().body;
       return data;
    });
}


export default class HomeController extends Controller {


  index(application, request, reply, callback) {
    this.context.cookie.set('random', '_' + (Math.floor(Math.random() * 1000) + 1), { path: '/' });
    this.context.data = { random: Math.floor(Math.random() * 1000) + 1 };
    callback(null);
  }

  toString(callback) {

    // Works 
    let context = getData(this.context);
    // Doesn't work
    // let context = fetchData(this.context);

    context.data = this.context.data;

    nunjucks.render('components/pages/Home/home.html', context, (err, html) => {
      if (err) {
        return callback(err, null);
      }

      callback(null, html);
    });
  }

  attach(el) {
    console.log(this.context.data.random);
    this.clickHandler = el.addEventListener('click', onClick, false);
  }

  detach(el) {
    el.removeEventListener('click', onClick, false);
  }

}

Is it possible to have the data fetched before the the page renders? I am trying to keep things as vanilla as possible, because I am trying to learn as much as possible. I've been stuck for days trying to solve this problem, so I am coming to SO for help, and to help others who have the same problem. My issue is similar to this issue, https://github.com/reactjs/redux/issues/99 but I am not trying to use redux, would rather use promises instead.

mchavezi
  • 482
  • 1
  • 4
  • 14
  • 3
    Stop thinking in terms of synchronous function calls and execution when you are making asynchronous calls. Instead start thinking in terms of callbacks and events. Welcome to the async world. – Darin Dimitrov May 15 '16 at 20:29
  • Can you provide a little more context around how your code will be executed. You mention "program", but that's a little vague. Is it a program in Node? Is it just JavaScript being run in an HTML page? Are you writing a function? Are you using other frameworks? – Saeed Jahed May 15 '16 at 21:03
  • Sorry to be vague. Here is a link to the real life project that I am trying to use the function in: https://github.com/bigbassroller/isomorphic-js/blob/master/src/components/pages/Home/HomeController.js getData needs to return an object to be consumed by the controller inside the tostring(callback) function. Thanks everyone for your help. I realize I am in over my head but this is what interest me at the moment and I would really like to solve and understand this problem. – mchavezi May 15 '16 at 23:02
  • I reworded my question with the context of the project i am working on. – mchavezi May 15 '16 at 23:13

2 Answers2

0

When using async calls you can't have a guarantee of when the call will return (therefore async). Which means that if you want something done after the data is returned the place to do it is inside the "then" clause.

Could you please explain some more on your usecase here?

Natashz
  • 71
  • 1
  • 3
  • Thanks for the tip, I'll mess around with that idea. The use case is for an isomorphic web app based on the book "Building Isomorphic Javascript Web Apps". The data needs to be serialized at render time to be used on both server and client. – mchavezi May 15 '16 at 20:38
0

It is not possible. You'd need to change your program design to work with this. Here is a simple example:

Suppose you have some function foo() that returns a string:

function foo() {
  x = fetchSync();
  return x;
} 

Now suppose you don't have fetchSync() and you're forced to do the work asynchronously to compute the string to return. It is no longer possible for your function to have the string ready to return before the end of the function is reached.

So how do you fix it? You redesign the foo() function to be asynchronous too.

function foo(callback) {
  // kick off fetch
  fetch(function(response) {
    // call callback() with the
    // the results when fetch is done
    callback(response.json())
  });
}

Same example using Promises:

function foo() {
  return fetch().then(function(response) {
     return response.json();
  });
}

Generally most environments that run JavaScript will support asynchronous designs. For example, in Node, a JavaScript program will not finish running if there is callbacks registered that can still be called.

Saeed Jahed
  • 821
  • 5
  • 8
  • Thanks Saeed for your detailed explanation:) I modified what I was working with your code to function fetchData(context) { return fetch("http://jsonplaceholder.typicode.com/users/1").then(function(response) { let data = response.json().body; return data; }); } The app doesn't crash, but it appears the data is not available in the nunjucks template. What would be the best way to see if the data is being passed in? When I console.log data, it says undefined. – mchavezi May 15 '16 at 23:24
  • I'm on the run, but here's a quick thing I put together that might help: http://jsbin.com/woyojunihu/edit?html,js,output – Saeed Jahed May 16 '16 at 00:03