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.
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?
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.
Related
NOTICE: This is probably not a duplicate since I have a much different requisite, and I couldn't find the solution yet.
Here's the scenario:
I'm using a custom component which has a getSignature function.
About this function:
It must be synchronous, adding async key word will not work.
This function will be automatically called while the component is mount.
This function must returns some data for further use.
But now the signature must be generated synchronously(from an HTTP request), and can't be generated before this component is actually mount.
Here's the simplified code:
class Showcase extends UniqueComponent {
getSignature() {
function runAsyncSynchronously(asyncFunc) {
// Is this possible?
}
function getData() {
return new Promise(resolve => {
setTimeout(() => resolve(42), 3000);
});
}
let data = runAsyncSynchronously(getData);
return data; // output "42" after 3 seconds
}
}
Is this possible to make this happen?
Doesn't matter if the whole process is frozen while waiting.
I tried this but it just got stuck forever...
function runAsyncSynchronously(asyncFunc) {
let isDone = false;
let result = null;
asyncFunc().then(data => {
isDone = true;
result = data;
});
while (!isDone){
}
return result;
}
I'm trying to make an api call using the callback method in request, but I'm new to web development and I'm stuck on async at the moment. I've got the following working, but I want to break the logic out some. For example, here's what is working currently
const request = require('request');
class GetAllData{
constructor(){
this.loadData();
}
// Gets all data from api query that I'll need to build everything else I'll need
data(callback){
request({'url':`https://definitely/a/url.json`, 'json': true }, function (error, response, body) {
callback(body);
});
}
loadData(cmrUrl){
console.log("loadData");
this.data(function(result){ console.log(result.foo.bar)});
}
}
var moreData = new GetAllData();
This works, and I can do the two things I need, which are log some results, and make a small calculation with the results. However, if I want to break this logic out into other functions, I get some errors.
const request = require('request');
class GetAllData{
constructor(){
this.loadData();
// Member variables
this._subsetOne;
this._thingICalculated;
// Function call to print data outside of the async call.
this.printData(this._subsetOne, this._thingICalculated);
}
// Gets all data from api query that I'll need to build everything else I'll need
data(callback){
request({'url':`https://definitely/a/url.json`, 'json': true }, function (error, response, body) {
callback(body);
});
}
loadData(cmrUrl){
console.log("loadData");
// Set a class member variable
// ERROR: 'this' is undefined
this.data(function(result){ this._subsetOne = result.foo.bar)};
// Call a member function, which calculates something, and then sets a member variable.
this.calculateSomething = result;
console.log(result);
};
}
// Function which takes in result from async call, then calculates something.
set calculateSomething(result){
this._thingICalculated = result + 1;
}
printData(x, y){
console.log(x,y);
}
}
var moreData = new GetAllData();
From what I've been reading the issues I'm hitting are pretty common, but I'm still not understanding why this isn't working since the call is asyncronous, and I'm just trying to set a variable, or call a function. I'm assuming there's some way to ask the member variable setting and function call to await the completion of the async request?
Fix attempt one
const request = require('request');
class GetAllData{
constructor(){
this.loadData();
this._subset;
}
// Gets all data from api query that I'll need to build everything else I'll need
data(callback){
request({'url':`https://definitely.a.url/yep.json`, 'json': true }, function (error, response, body) {
callback(body);
});
}
loadData(cmrUrl){
console.log("loadData");
this.data(function(result){ this._subset = result
this.getSubset()}.bind(this));
}
getSubset(){
console.log(this._subset);
}
}
var moreData = new GetAllData();
Subset ends up being undefined.
In the constructor, you have to bind any member method that uses this to itself. So before calling loadData:
this.loadData = this.loadData.bind(this);
Also seems like this.data will get its own scope in loadData, which is why this returns undefined inside that function. Thus you have to bind this.data to this (your class instance) as well.
And do the same for any method that accesses this. The problem is classic JavaScript functions by default have an undefined scope. Arrow functions however automatically inherit the scope of the caller.
I'd like to accomplish the following using promises: only execute further once the state of something is ready. I.e. like polling for an external state-change.
I've tried using promises and async-await but am not getting the desired outcome. What am I doing wrong here, and how do I fix it?
The MDN docs have something similar but their settimeout is called within the promise--that's not exactly what I'm looking for though.
I expect the console.log to show "This function is now good to go!" after 5 seconds, but instead execution seems to stop after calling await promiseForState();
var state = false;
function stateReady (){
state = true;
}
function promiseForState(){
var msg = "good to go!";
var promise = new Promise(function (resolve,reject){
if (state){
resolve(msg);
}
});
return promise;
}
async function waiting (intro){
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
What you're doing wrong is the promise constructor executor function executes immediately when the promise is created, and then never again. At that point, state is false, so nothing happens.
Promises (and async/await) are not a replacement for polling. You still need to poll somewhere.
The good news: async functions make it easy to do conditional code with loops and promises.
But don't put code inside promise constructor executor functions, because of their poor error handling characteristics. They are meant to wrap legacy code.
Instead, try this:
var state = false;
function stateReady() {
state = true;
}
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
async function promiseForState() {
while (!state) {
await wait(1000);
}
return "good to go!";
}
async function waiting(intro) {
var result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Based on your comments that you are waiting for messages from a server it appears you are trying to solve an X/Y problem. I am therefore going to answer the question of "how do I wait for server messages" instead of waiting for global variable to change.
If your network API accepts a callback
Plenty of networking API such as XMLHttpRequest and node's Http.request() are callback based. If the API you are using is callback or event based then you can do something like this:
function myFunctionToFetchFromServer () {
// example is jQuery's ajax but it can easily be replaced with other API
return new Promise(function (resolve, reject) {
$.ajax('http://some.server/somewhere', {
success: resolve,
error: reject
});
});
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
If your network API is promise based
If on the other hand you are using a more modern promise based networking API such as fetch() you can simply await the promise:
function myFunctionToFetchFromServer () {
return fetch('http://some.server/somewhere');
}
async function waiting (intro){
var result = await myFunctionToFetchFromServer();
console.log(intro + result);
}
Decoupling network access from your event handler
Note that the following are only my opinion but it is also the normal standard practice in the javascript community:
In either case above, once you have a promise it is possible to decouple your network API form your waiting() event handler. You just need to save the promise somewhere else. Evert's answer shows one way you can do this.
However, in my not-so-humble opinion, you should not do this. In projects of significant size this leads to difficulty in tracing the source of where the state change comes form. This is what we did in the 90s and early 2000s with javascript. We had a lot of events in our code like onChange and onReady or onData instead of callbacks passed as function parameters. The result was that sometimes it takes you a long time to figure out what code is triggering what event.
Callback parameters and promises forces the event generator to be in the same place in the code as the event consumer:
let this_variable_consumes_result_of_a_promise = await generate_a_promise();
this_function_generate_async_event((consume_async_result) => { /* ... */ });
From the wording of your question you seem to be wanting to do this instead;
..somewhere in your code:
this_function_generate_async_event(() => { set_global_state() });
..somewhere else in your code:
let this_variable_consumes_result_of_a_promise = await global_state();
I would consider this an anti-pattern.
Calling asynchronous functions in class constructors
This is not only an anti-pattern but an impossibility (as you've no doubt discovered when you find that you cannot return the asynchronous result).
There are however design patterns that can work around this. The following is an example of exposing a database connection that is created asynchronously:
class MyClass {
constructor () {
// constructor logic
}
db () {
if (this.connection) {
return Promise.resolve(this.connection);
}
else {
return new Promise (function (resolve, reject) {
createDbConnection(function (error, conn) {
if (error) {
reject(error);
}
else {
this.connection = conn; // cache the connection
resolve(this.connection);
}
});
});
}
}
}
Usage:
const myObj = new MyClass();
async function waiting (intro){
const db = await myObj.db();
db.doSomething(); // you can now use the database connection.
}
You can read more about asynchronous constructors from my answer to this other question: Async/Await Class Constructor
The way I would solve this, is as follows. I am not 100% certain this solves your problem, but the assumption here is that you have control over stateReady().
let state = false;
let stateResolver;
const statePromise = new Promise( (res, rej) => {
stateResolver = res;
});
function stateReady(){
state = true;
stateResolver();
}
async function promiseForState(){
await stateResolver();
const msg = "good to go!";
return msg;
}
async function waiting (intro){
const result = await promiseForState();
console.log(intro + result)
}
setTimeout(stateReady,5000);
waiting("This function is now ");
Some key points:
The way this is written currently is that the 'state' can only transition to true once. If you want to allow this to be fired many times, some of those const will need to be let and the promise needs to be re-created.
I created the promise once, globally and always return the same one because it's really just one event that every caller subscribes to.
I needed a stateResolver variable to lift the res argument out of the promise constructor into the global scope.
Here is an alternative using .requestAnimationFrame().
It provides a clean interface that is simple to understand.
var serverStuffComplete = false
// mock the server delay of 5 seconds
setTimeout(()=>serverStuffComplete = true, 5000);
// continue until serverStuffComplete is true
function waitForServer(now) {
if (serverStuffComplete) {
doSomethingElse();
} else {
// place this request on the next tick
requestAnimationFrame(waitForServer);
}
}
console.log("Waiting for server...");
// starts the process off
requestAnimationFrame(waitForServer);
//resolve the promise or whatever
function doSomethingElse() {
console.log('Done baby!');
}
I am trying to call a function to get a value from a 'subproduct' table and insert it in to another table. However the value which I am returning is not fetching the latest value from table and it is getting returned even before the snapshot part of the function is getting executed. I want it to run synchronously. Is there a better way in which it can be written.
function getGSTvalues(para1) {
var gstVar = 1;
var gstVarPromise = SubProductRef.once("value").then(function(snapshot) {
snapshot.forEach(function(child) {
if (para1 == child.val().subproductName) {
gstvar = child.val().gst;
console.log("InsidePromise" + gstVar);
}
});
console.log("outside fun : " + gstVar);
});
console.log("outside fun1 : " + gstVar);
return gstVar;
};
This is where I am calling the above function:
var gstans = getGSTvalues($('#edit_ProductSubType').val());
Any help would be appreciated
Using synchronous logic would be a big step backwards. The best solution here would be to use the asynchronous pattern correctly and provide a callback function to getGSTvalues() which is executed after the async operation completes and receives the result as an argument. Try this:
function getGSTvalues(para1, cb) {
var gstVar = 1;
var gstVarPromise = SubProductRef.once("value").then(function(snapshot) {
snapshot.forEach(function(child) {
if (para1 == child.val().subproductName) {
gstVar = child.val().gst;
}
});
cb && cb(gstVar);
});
};
getGSTvalues($('#edit_ProductSubType').val(), function(gst) {
console.log(gst);
// work with the value here...
});
Another alternative would be to return the promise from SubProductRef from getGSTvalues() and apply then() on that in the calling scope, although this would render the function largely redundant.
Also note that JS is case sensitive so gstVar is not the same as gstvar. I corrected this above.
I have a question regarding ES6 modules and how to correctly call functions between them as a callback.
Take "page_API.js", Upon data being recieved the callback function is called
// Make a call to our server, Once we've recieved data we'll call our callback
import {requestExecuteAsync} from "../xml_functions";
export const getData = () => {
requestExecuteAsync('api/getData', "dataRecieved");
};
export const dataRecieved = () => {
alert('Recieved Data');
};
Now in my "xml_functions.js" where I handle this requestExecuteAsync and more, I would like to call the dataRecieved once the server has responded.
Previously the codebase I work with consisted of many JS files, with all functions living in the global namespace, so the function worked like this
// once data has been retrieved from server
if (callbackparamsArr.length) {
window[callback](res, callbackparamsArr);
} else {
window[callback](res);
}
However now the callback function is undefined in the window as it no longer has scope of dataRecieved.
I've tried including the dataRecieved function inside the xml_functions
import { dataRecieved } from "../MyPage/MyPage_API.js";
and then just call
[callback](res)
but due to the "dataRecieved" import getting given a different string as defined in requestExecuteAsync (E.G it will be called "_Data_Recieved_" instead of "dataRecieved" i'm not sure what to do.
Any help would be much appreciated!
Thanks
You should not pass the name of the callback function you want to call. Just pass the function itself:
import {requestExecuteAsync} from "../xml_functions";
export function getData() {
requestExecuteAsync('api/getData', dataReceived);
// ^^^^^^^^^^^^
}
export function dataReceived() {
alert('Recieved Data');
}
export function requestExecuteAsync(path, callback) {
…
if (typeof callback == "function") callback(res);
…
}
But since you're using ES6, you might want to have a look at promises so that you don't need to pass callback functions around at all.