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.
Related
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.
What does below syntax means?
connect(mapStateToProps, mapDispatchToProps)(Home)
I understand we are passing two arguments to a function, but what is the purpose of below one?
(Home)
It doesn't look like node but Redux and as in a comment not an ES6 thing.
What it is: Connect is a higher order (factory) function ie. it returns a function. And it is that returned function which is immediately called with Home
Take a look and an example of mock of connect below
function connect(param1, param2) {
return innerFunction (innerParam) {
console.log(`${param1} ${innerParam} ${param2}`)
}
}
connect('A','B')('precedes')
// outputs 'A precedes B'
Edit: Added an example.
A function can return a function, and you can call this returned function immediately.
For information and as already stated in comments, the fact of decomposing one function call into smaller like this one in your example is called currying and is a common practice in JavaScript (more info here : What is 'Currying'?)
This example might help you :
function function1(info) {
return function(innerParam) {
console.log(`Hello this function has info ${info} and has just been called with this param: ${innerParam}` )
}
}
function1('Bobby')('Alice');
// same as :
var bobbyFunction = function1('Bobby');
bobbyFunction('Alice');
This is useful to dynamically generate a function that depends on some parameter, but can still be called several time with some other changing parameters. Imagine this, for instance :
var bobbyFunction = function1('Bobby');
['Alice', 'Tommy', 'Johny'].forEach(name => bobbyFunction(name));
It's plain javascript. Function connect returns another function and code immediately calls it with parameter Home.
function first(f) {
return function second(s) {
console.log(f, s);
}
}
// this
first('one')('two');
// is same as this
var x = first('one');
x('two');
see this example:
connect(mapStateToProps, mapDispatchToProps)(Home)// just like code below
function f(){
//do something
}
function connect(a,b){
return f;
}
connect(mapStateToProps, mapDispatchToProps);//first,return f;
connect(mapStateToProps, mapDispatchToProps)(Home)//second,return f(home);
I am trying to write unit tests for a function that reads a jsonfile into an object. I read the file with
jsonfile.readFile(filename, function (err, obj) {
//...
});
For my unit tests, I want to mock this function so that, rather than actually reading the file, it will simply return a fixed json block and pass it into the callback.
What I'm having trouble with is how to mock the function. I've seen sinon, which says it supports mocking functions, but I can't find anything that describes how to actually define custom behavior for the function I'm mocking. Sinon looks like it allows me to define what I want the function to return, how often I expect it to be called, etc, but not actually define a mocked function.
Basically, I want something like this:
mock(jsonfile, 'readFile', function(filename, callback) {
callback(null, {attr1:"foo"});
});
How do I do this with sinon?
But actually, why don't you just replace readFile by a function with the same definition (so that it doesn't break the code using it). And just return your mock data.
jsonfile.readFile = function(filePath, callback) {
callback(null, { mockData: "foo" });
};
easy as that.
Otherwise, you can use a Proxy if you don't want to deal with the definition :
const jsonfile = {
readFile: function(filename, callback) {
callback();
}
};
// intercept every call to readFile and always return the mock data
jsonfile.readFile = new Proxy(jsonfile.readFile, {
apply: function(target, thisArg, args) {
return args[1](null, { someMocking: "" });
}
});
// call readFile as usual
jsonfile.readFile('testfile', function(err, result) {
console.log(result);
});
Proxies work as interceptors.
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Proxy
This is not straightforward in testing because it involved callbacks. You need to test wether a callback you passed to readFile was called with right arguments, which in this case is the dummyFile.
import sinon from 'sinon'
import jsonfile from './jsonfile'
const jsonFileMock = sinon.spy(jsonfile, 'readFile');
const callback = sinon.spy();
jsonfile.readFile(callback);
jsonFileMock.callsArgWith(1, 'dummyFileName');
expect(callback.calledWith('dummyFileName')).to.be.true;
jsonFileMock.restore();
If you want to abstract this into a function, than it can be something like :
function mock(module, method, ...callbacks){
const stub = sinon.stub(jsonfile, 'readFile');
callbacks.forEach((callback, index) => {
stub.callsArgWith(index, callback);
});
}
The function I was looking for is stub.callsFake():
> Thing = {
... meth : function() { console.log(1) }
... }
> Thing.meth()
1
> var stub = sinon.stub(Thing, 'meth')
> stub.callsFake(function() { console.log(2) })
> Thing.meth()
2
> stub.restore()
> Thing.meth()
1
It doesn't look like mock is capable of what I want to do.
I have some API (LoadFileApi.load) for load file that fires callback on complete. There is common logic (prepare) and two different ways of processing (fired by click handlers) that are triggered upon prepare. I wonder, how to make it in clear and convenient, JS way?
function loadFile (onCompleteFileLoad) {
LoadFileApi.load({
url: 'url',
onComplete: filePreparing
});
}
function fileProcessing1() {
}
function fileProcessing2() {
}
// common logic
function filePreparing(file) {
// prepare
...
// after prepare I need to run file processing routine corresponding to each handler
}
function clickHandlerA() {
loadFile(filePreparing);
// needs to trigger fileProcessing1
}
function clickHandlerB() {
loadFile(filePreparing);
// needs to trigger fileProcessing2
}
Simple decision is to declare shared variable and set it in each handler before file load.
function filePreparing(file) {
// prepare
...
if (processingMethod == 1) {
fileProcessing1();
} else {
fileProcessing2();
}
}
function clickHandlerA() {
processingMethod = 1;
loadFile(filePreparing);
}
function clickHandlerB() {
processingMethod = 2;
loadFile(filePreparing);
}
var processingMethod;
But it seems to me that more elegant JS way should exists... It would be great if there is no conditional choosing of processing function in PREPARE. It should be passed as parameter somehow...
I believe you want something similar to the answer to this question - JavaScript: Passing parameters to a callback function
Using the pattern described in the answer in the link above you will see that you can simply pass in parameters into your callback generically.
something like:
function clickHandlerB() {
loadFile(filePreparing, fileProcessing1);
}
instead of passing a string flag to indicate which function to call just pass the function itself. The filePreparing function's signature will need to be updated to include the callback function
function filePreparing(file, callback)
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.