This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I'm facing a problem since this morning.
WHAT I HAVE
Basically, I have a simple class, with an array of files to register:
function MyClass() {
this.filesToRegister = [
{
"fileName": "http://tny.im/azk"
},
{
"fileName": "http://tny.im/azk"
}
];
}
I have also a simple function, _contextFunction() which takes a single fileToRegister entry:
MyClass.prototype._contextFunction = function(fileToRegister) {
return new Promise((resolve, reject) => {
logger.info(typeof this.filesToRegister);
logger.info('current file: ' + fileToRegister);
return resolve();
});
};
Note that this function MUST access to the context (the this), it's mandatory, and I can't change that.
Finally, I have a utility method, processArray(), that can apply a function on each item of an array, all done synchronously:
MyClass.prototype.processArray = function(array, fn) {
let results = [];
return array.reduce((p, item) => {
return p.then(() => {
return fn(item).then((data) => {
results.push(data);
return results;
}).catch(err => console.log(err));
});
}, Promise.resolve());
};
WHAT I TRY TO DO
I use this utility method to apply _contextFunction() on each item of the filesToRegister array:
this.processArray(this.filesToRegister, this._contextFunction);
It works without problem and execute this._contextFunction() on each item of this.filesToRegister.
WHAT THE PROBLEM IS
BUT, when I try to log typeof this.filesToRegister in _contextFunction(), the result is undefined... After several tests, I concluded that nothing in the context is accessible (neither context attributes nor context methods).
However, if I execute this._contextFunction() without the processArray() method, I can access to the context (both context attributes and context methods).
WHAT I THINK
My guess is that the problem comes from the processArray() method, but I don't see where... I tried to log typeof this.filesToRegister right in the processArray() method, and it works...
To conclude:
processArray() IS able to access to the context.
this._contextFunction() launched 'standalone' IS able to access to the context.
this._contextFunction() launched by processArray() IS NOT able to access to the context.
Can anyone help me? Thanks
fn(item)
Calls the function without the context. Use:
fn.call(this, item)
Alternatively pass the method name:
this.processArray(this.filesToRegister,"_contextFunction");
And then do:
this[fn](item);
How i would do that:
class MyClass {
constructor(){
this.files = [];
}
async add(file){
await "whatever";
this.files.push(file);
}
multiple(name, array){
return Promise.all( array.map(el => this[name](el)));
}
}
And then:
const instance = new MyClass;
instance.add("whatever");
instance.multiple("add",[1,2,3]);
Related
I have an method in my JS class and in the callback of a Promise, I want it to call another class method.
class MyClass {
myClassMethod(arg1) {
// this method did get called
}
aSecondClassMethod() {
//...
}
methodWithPromise() {
var myClassMethod = this.myClassMethod;
let aPromise = methodReturnPromise();
aPromise.then(function (value) {
myClassMethod(value);
}
}
So I create a var calls myClassMethod and set that to this.myClassMethod.
And when I debug the code, myClassMethod did get called in the then callback of the Promise.
The problem I am having is when my myClassMethod() calls other class method(), i.e.
myClassMethod(args) {
aSecondClassMethod();
}
I get error saying aSecondClassMethod is undefined. I tried
myClassMethod(args) {
this.aSecondClassMethod();
}
But it gives me the same error. I think I can work around this by declaring a var for each of the class method that myClassMethod() calls.
var aSecondClassMethod= this.aSecondClassMethod;
But that seem cumbersome to maintain the code going forward.
I would like to know if there is a better way to do this.
Use an arrow function, as it captures the this value of the enclosing context.
aPromise.then(value => this.myClassMethod(value));
I would also recommend using the new () => {} function notation for defining the class method that contains the promise. Without that (or an old school bind) this will still be undefined.
My usual style:
class MyClass {
myClassMethod = (value) => {
//something..
};
methodWithPromise = () => {
somePromise
.then((res) => {
this.myClassMethod(res);
})
.catch((err) => {
console.error(err);
return;
});
};
}
I'm relatively new to js so please forgive me if my wording isn't quite right. I've also created a jsfiddle to demonstrate the issue.
Overview
In the app I'm working on, I have a function with a jquery ajax call, like this:
function scenario1(ajaxCfg) {
return $.ajax(ajaxCfg)
}
I want to change this function, but without in any way changing the inputs or outputs (as this function is called hundreds of times in my application).
The change is to make a different ajax call, THEN make the call specified. I currently have it written like this:
function callDependency() { //example dependency
return $.ajax(depUri)
}
function scenario2(ajaxCfg) {
return callDependency().then(() => $.ajax(ajaxCfg))
}
Desired Result
I want these two returned objects to be identical:
let result1 = scenario1(exampleCall)
let result2 = scenario2(exampleCall)
More specifically, I want result2 to return the same type of object as result1.
Actual Result
result1 is (obviously) the result of the ajax call, which is a jqXHR object that implements the promise interface and resolves to the same value as result2, which is a standard promise.
Since result2 is not a jqXHR object, result2.error() is undefined, while result1.error() is defined.
I did attempt to mock up these methods (simply adding a .error function to the return result, for example), but unfortunately even when doing this, result1.done().error is defined while result2.done().error is undefined.
Wrapping (or unwrapping) it up
In a nutshell, I want to return the jqXHR result of the .then() lambda function in scenario2 as the result of the scenario2 function. In pseudocode, I want:
function scenario2(ajaxCfg) {
return callDependency().then(() => $.ajax(ajaxCfg)).unwrapThen()
} //return jqXHR
What about something like this? The approach is a little different, but in the end you can chain .done() etc. to the scenario2() function:
const exampleCall = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
const depUri = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
function callDependency() { //example dependency
return $.ajax(depUri).done(() => console.log('returned callDependancy'))
}
let obj = { //creating an object with the scenario2 as a method so that I can bind it with defer.promise()
scenario2: function(ajaxCfg) {
return $.ajax(ajaxCfg).done(() => console.log('returned senario2')) // Purposely NOT calling the exampleCall() function yet
}
}
defer = $.Deferred(); // Using some JQuery magic to be able to return a jqXHR
defer.promise(obj); // Set the object as a promise
defer.resolve(callDependency()); // Invoking the callDependency() by default on promise resolve
obj.done(() => {
obj.scenario2() // Resolving so the callDependency() function can be called
}).scenario2(exampleCall).done(() => { // Here you can invoke scenario2 and FINALLY chain whatever you want after everything has been called
console.log('Here I can chain whatever I want with .done\(\) or .fail\(\) etc.')
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
What I think is cool about this way of doing it is that you can just keep adding methods to the object that you created, and then all your secondary functions that are built on top of callDependency() can be in one place. Not only that, but you can reuse those same methods on top of other AJAX calls.
Read more about this here.
I hope this helps!
I feel like your life would be made a lot easier if you used async/await syntax. Just remember though that async functions return a promise. So you could instead write:
async function scenario2(ajaxCfg) {
let jqXhrResult;
try {
await callDependency();
jqXhrResult = {
jqXhr: $.ajax(ajaxCfg)
};
} catch() {
// Error handling goes here
}
return jqXhrResult;
}
I actually thought of a way easier way to do this.
You can do it by adding a method to the function constructor's prototype object. That way any created function can inherit that method and you can still use the .done() syntax. It's referred to as prototypal inheritance:
const exampleCall = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
const depUri = { url: 'https://code.jquery.com/jquery-1.12.4.min.js'};
function callDependency() {
return $.ajax(depUri).done(() => console.log('returned callDependancy'))
}
Function.prototype.scenario2 = function(ajaxCfg, ...args) {
return this(...args).then(() => $.ajax(ajaxCfg))
}
callDependency.scenario2(exampleCall).done(data => {
console.log(data)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I am confused on this one because every tutorial I have found so far assumes I can edit the library code, or that the library only has call backs or the call back as the last parameter.
The library I am using has every function set up like
function(successCallBack(result), FailCallBack(error),options)
So in each instance, I end up using code like
var options={stuff1:1, stuff2:2};
doStuff(success,failure,options);
function success(result){
//handle, usually with another call to a similar function, chaining callbacks together
};
function failure(error){
//handle error
};
How do I convert these into promises when I only have control of the call, and the success and failures?
Also, as a bonus, the chains are accessing variables outside them.
var options={stuff1:1, stuff2:2};
doStuff(success,failure,options);
function success(result){
var options2={stuff1:1, stuff2:2};
doStuff2(function(result2){
processStuff(result1, result2);
},function(error){
//handle error
},options2)
};
function failure(error){
//handle error
};
function processSuff(result1,result2){
//do things to results
}
Thanks
You could use the following function. It accepts a function to promisify and options, and returns promise:
let promisify = (fn, opts) => {
return new Promise((resolve, reject) => {
fn(resolve, reject, opts);
});
}
It could be used as follows:
promisify(doStuff, options)
.then(data => console.log('Success call', data))
.catch(err => console.log('Error', err));
Rather than call promisify every time you want to use your function, you can wrap it once and get a new function that you can just use as needed. The new function will return a promise and resolve/reject instead of the success and fail callbacks. This particular version of promisify is specifically coded for the calling convention you show of fn(successCallback, errorCallback, options). If you have other functions with different calling conventions, then you can create a different promisify version for them:
// return a promisified function replacement for this specific
// calling convention: fn(successCallback, errorCallback, options)
function promisify1(fn) {
return function(options) {
return new Promise((resolve, reject) => {
fn(resolve, reject, options);
});
}
}
So, then you would use this like this:
// do this once, somewhere near where doStuff is imported
let doStuffPromise = promisify1(doStuff);
// then, anytime you would normally use doStuff(), you can just use
// doStuffPromise() instead
doStuffPromise(options).then(results => {
// process results here
}).catch(err => {
// handle error here
});
The advantage of doing it this way is that you can "promisify" a given function of set of functions just once in your project and then just use the new promisified versions.
Suppose you imported an object that had a whole bunch of these types of interfaces on it. You could then make yourself a promisifyObj() function that would make a promisified interface for all the functions on the object.
// a "Promise" version of every method on this object
function promisifyAll(obj) {
let props = Object.keys(obj);
props.forEach(propName => {
// only do this for properties that are functions
let fn = obj[propName];
if (typeof fn === "function") {
obj[propName + "Promise"] = promisify1(fn);
}
});
}
So, if you had an object named myModule that had all these methods with this non-promise interface on it, you could promisify that module in one step:
promisifyAll(myModule);
And, then you could use any method from that object by just adding the "Promise" suffix to the method name:
myModule.someMethodPromise(options).then(results => {
// process results here
}).catch(err => {
// handle error here
});
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I have a module like this.
somemodule.js
module.exports = {
val: null,
get: function() {
finddata('/', function(resp) {
this.val = resp
}
}
}
and is called like this:
var x = require('somemodule');
x.get();
x.get();
After the 1st get call, this x.val is not being set. Tried this as well which does not work:
module.exports = {
val: null,
get: function() {
var that = this;
finddata('/', function(resp) {
that.val = resp
}
}
}
How do I set x.val?
Your finddata is running asynchronously , it's getting called and returned back immediately to continue next line execution . That moment its not sure callback is executed or not . Once callback is executed then only value will be set . To make sure value is set and then after getting the value , you can use promises.
I have just taken two sample file a.js and b.js to explain how it works
a.js
module.exports = {
val:null,
get:function(){
var that = this;
return new Promise(function(resolve, reject) {
that.finddata('/', function(resp){
that.val = resp;
resolve()
})
});
},
finddata :function(path,callback){
setTimeout(function() {
console.log("Lets wait for some time");
callback(10);
}, 100)
}
}
b.js
var x = require('./a');
x.get().then(function(){
console.log(x.val)
});
Output
Lets wait for some time
10
First of all, problem is not about requiring something and it is not related to scope. What is actually happening is, as already stated by others, finddata is asynchronous function meaning that you don't know at what time in future callback function (resp) {...} will be invoked and when val be something other than null. To tackle this you need either to pass additional callback to get function, or to return a promise from get function. Cleaner approach would be to return Promise from get function.
x.get()
.then(() => {
// val is ready
})
or
x.get(() => {
// val is ready
})
Another problem that you have is that you are not taking into account what if finddata invokes your callback with an error? Having something like:
finddata('/', function(resp){
that.val = resp
}
Is really something what you don't want to have. With code that you have, if finddata invokes your callback with an error, val would be equal to that error, otherwise it would be equal to null, if finddata complies to node best practices to invoke callback with null if there was no errors, such as cb(null, data).
Besides that what you are trying to do? Is there a need for exposing module with val thing? Is get function meant to be called regularly from app? If so why introducing new module, just call finddata which is i guess module by itself already.
This question already has answers here:
In ES2015 how can I ensure all methods wait for object to initialize ? With ES7 decorators?
(2 answers)
Closed 6 years ago.
Example:
var readBackend = function(){
var deferred = q.defer();
readData().then(function(data) {
deferred.resolve(data);
})
return deferred.promise;
}
class Test {
constructor(){
readBackend().then(function(data) {
this.importantData = data;
})
}
someFunc() {
//iterate over important data, but important data is defined
//promise didnt resolved yet
}
}
var test = new Test();
test.someFunc(); //throws exception!
Is there any way to ensure, that object properties are initiated by constructor, when I call someFunc?
The only way which comes to my mind is creating init function, which will return promise, but then, everytime I use my class, I would rely on init function to work properly
Is there any way to ensure, that object properties are initiated by constructor, when I call someFunc?
Not without restructuring your code. You cannot have your constructor perform asynchronous operations and expect your synchronous methods to work.
I see two possible solutions:
1. Have a static method which loads the data and returns a new instance of your class:
class Test {
static createFromBackend() {
return readBackend().then(data => new Test(data));
}
constructor(data){
this.importantData = data;
}
someFunc() {
// iterate over important data
}
}
Test.createFromBackend().then(test => {
test.someFunc();
});
This ensures that the data is available when the instance is created and you can keep the API of the class synchronous.
2. Store the promise on the object:
class Test {
constructor(){
this.importantData = readBackend();
}
someFunc() {
this.importantData.then(data => {
// iterate over important data
});
}
}
Of course if someFunc is supposed to return something, that would require it to return a promise as well. I.e. the API of your class is asynchronous now.