Asynchronous data loading in class constructor [duplicate] - javascript

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.

Related

How to access class method inside an anonymous function

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;
});
};
}

Can you write this without using a Deferred?

I wrote some code below that uses promises and the easiest way I could find to write it was using a Deferred object instead of the usual Promise executor function because I need to resolve the promise from outside the executor. I'm wondering if there's an accepted design pattern based on the Promise executor function for a problem like this that doesn't use a deferred-like solution? Can it be done without having to resolve the promise from outside the promise executor?
Here are the details.
I have a project that uses a set of Worker Threads and various parts of the code that want to use a Worker Thread from time to time. To manage that, I've created a simple WorkerList class that keeps a list of the available Worker Threads. When someone wants to use one, they call get() on it and that returns a promise that resolves to a Worker Thread. If a worker thread is available immediately, the promise resolves immediately. If all worker threads are in use (and thus the list of available workers is empty), then the promise doesn't resolve until one is later put back into the available list via the add(worker) method.
This WorkerList class has only two methods, add(worker) and get(). You get() a worker and when you're done with it, you add(worker) it back. When you add(worker) it back, the class checks to see if there are any tasks waiting for an available Worker. If there, are, it resolves their promise with an available Worker. That resolving of someone else's promise is where the Deferred was used.
Here's the code for the WorkerList:
class WorkerList {
constructor() {
this.workers = [];
this.deferredQueue = [];
}
add(worker) {
this.workers.push(worker);
// if someone is waiting for a worker,
// pull the oldest worker out of the list and
// give it to the oldest deferred that is waiting
while (this.deferredQueue.length && this.workers.length) {
let d = this.deferredQueue.shift();
d.resolve(this.workers.shift());
}
}
// if there's a worker, get one immediately
// if not, return a promise that resolves with a worker
// when next one is available
get() {
if (this.workers.length) {
return Promise.resolve(this.workers.shift());
} else {
let d = new Deferred();
this.deferredQueue.push(d);
return d.promise;
}
}
}
And, here's the Deferred implementation:
function Deferred() {
if (!(this instanceof Deferred)) {
return new Deferred();
}
const p = this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.then = p.then.bind(p);
this.catch = p.catch.bind(p);
if (p.finally) {
this.finally = p.finally.bind(p);
}
}
Maybe the below is just a poor man's approach to deferreds, and doesn't really get to the crux of the matter, but instead of a queue of deferreds, you could just keep a queue of resolver functions.
This saves a small amount of code over your approach and avoids explicitly using Deferreds.
I don't know if there is an established pattern for this, but this in itself seems like a reusable pattern for maintaining an asynchronous pool of objects, so rather than calling it WorkerList, you could name it AsyncPool, and then compose that as a reusable piece within your WorkerList:
class AsyncPool {
constructor() {
this.entries = [];
this.resolverQueue = [];
}
add(entry) {
console.log(`adding ${entry}`);
this.entries.push(entry);
// if someone is waiting for an entry,
// pull the oldest one out of the list and
// give it to the oldest resolver that is waiting
while (this.resolverQueue.length && this.entries .length) {
let r = this.resolverQueue.shift();
r(this.entries.shift());
}
}
// if there's an entry, get one immediately
// if not, return a promise that resolves with an entry
// when next one is available
get() {
return new Promise((r) =>
this.entries.length
? r(this.entries.shift())
: this.resolverQueue.push(r)
);
}
}
let pool = new AsyncPool();
pool.add('Doc');
pool.add('Grumpy');
pool.get().then(console.log);
pool.get().then(console.log);
pool.get().then(console.log);
pool.get().then(console.log);
// add more entries later
setTimeout(() => pool.add('Sneezy'), 1000);
setTimeout(() => pool.add('Sleepy'), 2000);
Here is one solution that doesn't expose the promise resolver function anywhere outside the promise executor function.
Following up on my comment to my own question about an event-based solution, here's what I came up with. It uses a triggered event and an event listener to cause an action inside the promise executor function.
class WorkerList extends EventEmitter {
constructor() {
this.workers = [];
}
add(worker) {
this.workers.push(worker);
// notify listeners that there's a new worker in town
this.emit('workerAdded');
}
// if there's a worker, get one immediately
// if not, return a promise that resolves with a worker
// when next one is available
get() {
if (this.workers.length) {
return Promise.resolve(this.workers.shift());
} else {
return new Promise(resolve => {
const onAdded = () => {
if (this.workers.length) {
this.off('workerAdded', onAdded);
resolve(this.workers.shift());
}
}
this.on('workerAdded', onAdded);
});
}
}
}
I was initially concerned about maintaining FIFO ordering so that the first one to call get() gets the next worker available. But, because eventListeners are called in the order they were added, I think this would actually achieve FIFO order. If there are multiple calls to get(), they will all get notified about the workerAdded, but after the first one handles the message and takes the worker, the others will just find no worker left for them so their listener will stay attached waiting for a future workerAdded message when there is a worker for them (when their listener gets to be first in line).
I don't think I necessarily like this better than the other options shown, but it is an alternative and doesn't use Deferreds or even expose the resolve handler outside the executor function.
As was suggested, this could also be done where the eventEmitter is an instance variable rather than a base class:
class WorkerList {
constructor() {
this.workers = [];
this.emitter = new EventEmitter();
}
add(worker) {
this.workers.push(worker);
// notify listeners that there's a new worker in town
this.emitter.emit('workerAdded');
}
// if there's a worker, get one immediately
// if not, return a promise that resolves with a worker
// when next one is available
get() {
if (this.workers.length) {
return Promise.resolve(this.workers.shift());
} else {
return new Promise(resolve => {
const onAdded = () => {
if (this.workers.length) {
this.emitter.off('workerAdded', onAdded);
resolve(this.workers.shift());
}
}
this.emitter.on('workerAdded', onAdded);
});
}
}
}

Return result of .then() lambda expression as function result

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>

Undefined 'this' in Promise + array. reduce() [duplicate]

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]);

NodeJS require scope variables [duplicate]

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.

Categories

Resources