In this code block:
if I declare resolve / reject with const keyword,
I need to pass them into executor function without this, and using this will get error;
if I declare resolve / reject with this keyword,
I need to pass them into executor function with this.
What cause this different behavior?
And what differences between these two ways?
Please help me, thanks~
class myPromise {
constructor(executor) {
this.status = PENDING;
this.value = null;
this.reason = null;
const resolve = value => {
this.value = value;
}
this.reject = reason => {
this.reason = reason;
}
// Why not use this.resolve / this.reject ?
//
executor(resolve, this.reject);
}
then(onfulfilled = Function.prototype, onrejected = Function.prototype) {
onfulfilled(this.value);
onrejected(this.reason);
}
}
Why not use this.resolve / this.reject ?
this.resolve wouldn't work because resolve is not part of this. Recall that variables declared with const are only accessible in block in which they are declared, which in this case, is constructor { ... } (docs).
On the other hand, when you say this.reject you are actually assigning that value to this, and have to use this. every time you want to refer to this value again.
Related
I was wondering what the best approach is for configuring a module export. "async.function" in the example below could be a FS or HTTP request, simplified for the sake of the example:
Here's example code (asynmodule.js):
var foo = "bar"
async.function(function(response) {
foo = "foobar";
// module.exports = foo; // having the export here breaks the app: foo is always undefined.
});
// having the export here results in working code, but without the variable being set.
module.exports = foo;
How can I export the module only once the async callback has been executed?
edit
a quick note on my actual use-case: I'm writing a module to configure nconf (https://github.com/flatiron/nconf) in an fs.exists() callback (i.e. it will parse a config file and set up nconf).
Your export can't work because it is outside the function while the foodeclaration is inside. But if you put the export inside, when you use your module you can't be sure the export was defined.
The best way to work with an ansync system is to use callback. You need to export a callback assignation method to get the callback, and call it on the async execution.
Example:
var foo, callback;
async.function(function(response) {
foo = "foobar";
if( typeof callback == 'function' ){
callback(foo);
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback = cb;
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Multiple callback way
If your module need to be called more than once you need to manage an array of callback:
var foo, callbackList = [];
async.function(function(response) {
foo = "foobar";
// You can use all other form of array walk.
for(var i = 0; i < callbackList.length; i++){
callbackList[i](foo)
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback.push(cb);
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Promise way
You can also use Promise to solve that. This method support multiple call by the design of the Promise:
var foo, callback;
module.exports = new Promise(function(resolve, reject){
async.function(function(response) {
foo = "foobar"
resolve(foo);
});
});
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js').then(function(foo){
//Here code using foo;
});
See Promise documentation
An ES7 approach would be an immediatly invoked async function in module.exports :
module.exports = (async function(){
//some async initiallizers
//e.g. await the db module that has the same structure like this
var db = await require("./db");
var foo = "bar";
//resolve the export promise
return {
foo
};
})()
This can be required with await later:
(async function(){
var foo = await require("./theuppercode");
console.log(foo);
})();
ES6 answer using promises:
const asyncFunc = () => {
return new Promise((resolve, reject) => {
// Where someAsyncFunction takes a callback, i.e. api call
someAsyncFunction(data => {
resolve(data)
})
})
}
export default asyncFunc
...
import asyncFunc from './asyncFunc'
asyncFunc().then(data => { console.log(data) })
Or you could return the Promise itself directly:
const p = new Promise(...)
export default p
...
import p from './asyncModule'
p.then(...)
Another approach would be wrapping the variable inside an object.
var Wrapper = function(){
this.foo = "bar";
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
});
}
module.exports = new Wrapper();
If the initializer has error, at least you still get the uninitialized value instead of hanging callback.
You can also make use of Promises:
some-async-module.js
module.exports = new Promise((resolve, reject) => {
setTimeout(resolve.bind(null, 'someValueToBeReturned'), 2000);
});
main.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// outputs 'someValueToBeReturned' after 2 seconds
The same can happen in a different module and will also resolve as expected:
in-some-other-module.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// also outputs 'someValueToBeReturned' after 2 seconds
Note that the promise object is created once then it's cached by node. Each require('./some-async-module') will return the same object instance (promise instance in this case).
Other answers seemed to be partial answers and didn't work for me. This seems to be somewhat complete:
some-module.js
var Wrapper = function(){
this.callbacks = [];
this.foo = null;
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
this.callbacks.forEach(function(callback){
callback(null, wrapper.foo);
});
});
}
Wrapper.prototype.get = function(cb) {
if(typeof cb !== 'function') {
return this.connection; // this could be null so probably just throw
}
if(this.foo) {
return cb(null, this.foo);
}
this.callbacks.push(cb);
}
module.exports = new Wrapper();
main.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined
});
main2.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined in another script
});
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I am writing some Node code with ES6. In my code, I have a class, that looks like the following. Please note, this is a basic example intended to isolate and show the problem.
class MyClass {
constructor() {
this.id = uuid();
this.number = Math.floor((Math.random() * 100) + 1);
}
process() {
this.validateNumber()
.then(this.generateImage)
.catch(function(err) {
console.log(err);
})
;
}
validateNumber() {
let self = this;
var promise = new Promise(function(resolve, reject) {
// do stuff with self.number
resolve({});
});
}
generateImage() {
console.log(this);
// the above prints "undefined" in the console window
// how do I get this.number again?
}
}
In this class, you'll notice that I generate a random number in my constructor. I want that number to be usable throughout the methods in my class. However, since I have one method that chains together promises, it's like this loses meaning after validateNumber is executed.
how do I resolve this?
ES6 introduced a feature called arrow functions. Apart from the syntax being more concise than the conventional function syntax, it retains the context of this. Essentially, your code can be changed to the following:
var promise = new Promise((resolve, reject) => {
console.log(this); // MyClass
resolve({});
});
And you also need to retain the context inside the then
this.validateNumber()
.then(() => this.generateImage());
I have two files in a folder - index.js and util.js with their code base as follows
Util.js
let obj = {}
obj.sendTransaction = () => {
console.log(arguments);
return new Promise((resolve, reject) => {
// try {
// let data = ethFunction.call()
// resolve(data)
// } catch (e) {
// reject(e)
// }
});
}
module.exports = obj
In Index.js, if I pass arguments to addNewParticipant or its variation then they do not turn up in the arguments object in util.js, for instance
const addNewParticipant = (foo, bar) => {
var ethFunction = myContract.addParticipant.sendTransaction
console.log(ethFunction);
EthUtil.sendTransaction()
}
const addNewParticipantTwo = (foo, bar) => {
var ethFunction = myContract.addParticipant.sendTransaction
console.log(ethFunction);
EthUtil.sendTransaction(ethFunction, foo, bar)
}
and call it such addNewParticpant(1, 2) and , addNewParticpantNew(1, 2) the numbers 1 and 2 do not show up in the arguments object in the util function. In fact, the arguments object remains the same, 4 inputs describing some functions and files in node_modules including Bluebird and a reference to index.js itself
My final aim is to
Pass a function from index.js to util.js
Pass along unknown number of variables
Call the passed function and apply the unknown number of variables to it
Wrap the whole thing in a promise and do some data validation
Ideally arguments[0] would represent a function I would pass and the other would be the values. I would then use
var result = arguments[0].apply(null, Array().slice.call(arguments, 1));
If it helps, the function I want to pass has an optional callback feature
As already mentioned in the comment, fat arrows don't have their own this or arguments objects. The arguments object you're logging is from the function created by the module loader, and its passed arguments.
You can either use a "regular function", or in this case, you can use a ...rest parameter
And, avoid the Deferred antipattern.
//first a little utility that might be handy in different places:
//casts/converts a value to a promise,
//unlike Promise.resolve, passed functions are executed
var promise = function(value){
return typeof value === "function"?
this.then( value ):
Promise.resolve( value );
}.bind( Promise.resolve() );
module.exports = {
sendTransaction(fn, ...args){
return promise(() => fn.apply(null, args));
}
}
The method, foo() is called, sx++ won't change. When I alert(sx) I get NaN. Should I define the method using prototype?
function fooClass(sx) {
this.sx = sx;
this.foo = function() {
if(booleanIsTrue) this.sx++;
};
}
*Ignore syntax errors, if any. This is not a copy paste. It is correct in my project.
Moving sx++ out side the if statement works.
Any ideas as to why this is happening?
As you said this looks like a case were you would want to use the prototype chain instead of creating new functions for each object created by the function. That would look like this
var FooClass = function (sx) {
this.sx = sx;
};
FooClass.prototype.foo = function () {
if (booleanIsTrue) { //Where is booleanIsTrue coming from?
this.sx++;
}
};
var a = new FooClass(0);
a.foo();
console.log(a.sx); //1
You're having this problem because you're adding to the wrong variable. You want to change the class variable sx.
As someone pointed out, class is a reserved word, usually you use klass instead.
Also, you should be using {}, try entering you code into JSLint and see what it returns.
Try this out:
function klass(sx) {
this.sx = sx;
this.foo = function(booleanIsTrue) {
if(booleanIsTrue === true) {
this.sx++;
}
};
}
var a = new klass(3);
a.foo(true);
console.log(a.sx); // 4
I am relatively new to JavaScript, and am having trouble grasping why this error occurs:
TypeError: Attempted to assign to readonly property. MyTimer.js: 35
I understand that this error is displayed because I am using strict mode, but I enabled strict mode to help me debug this Object.
The call to create the MyTimer singleton is:
var simTimer = new SimTimer();
Then I add a task to be executed in MyTimer as follows:
var task = function(){
console.log("performing task.");
};
simTimer.addTask(task);
Lastly, this is the MyTimer Object (line 35 is marked):
var MyTimer = (function () {
"use strict";
// var timer;
var tasks;
/**
* If an instance of MyTimer exists, it will be saved to this variable and returned
* by any subsequent calls to this constructor, thus only one instance (stored in this
* variable) will be accessible.
* #private
*/
var instance;
/**
* Called to initialize this MyTimer Object, or return #instance if it already contains
* an instance of this Object.
*/
function Singleton() {
if (instance) {
return instance;
}
instance = this;
tasks = $.Callbacks();
this.timer = setInterval(function()
{
this.tasks.fire();
}, 1000);//<---- THIS IS LINE 35!
this.addTask = function(task)
{
this.tasks.add(task);
};
this.removeTask = function(task)
{
this.tasks.remove(task);
};
}
//instance accessor
Singleton.getInstance = function () {
return instance || new Singleton();
};
return Singleton();
}());
What have I failed to grasp? I have read through a lot of documentation on Module Patterns, and have successfully written Singletons before - so where do I go wrong here?
** EDIT: **
I was able to get the correct behavior by removing var tasks, and creating it within Singleton using this. The working version of the function now looks like this:
function Singleton() {
if (instance) {
return instance;
}
instance = this;
this.tasks = $.Callbacks();
this.timer = setInterval(function(){
instance.tasks.fire();
}, 1000);
this.removeTask = function(task)
{
instance.tasks.remove(task);
};
this.addTask = function(task)
{
instance.tasks.add(task);
};
}
So I still don't fully understand - why did this change fix it? Was it a scope issue after all?
If I am reading your code right, you have a scope issue here
this.timer = setInterval(function()
{
this.tasks.fire(); <-- this will be in window scope
}, 1000);
It should be
this.timer = setInterval(function()
{
instance.tasks.fire();
}, 1000);
I believe the following strict mode restriction is the explanation.
If this is evaluated within strict mode code, then the this value is
not coerced to an object. A this value of null or undefined is not
converted to the global object and primitive values are not converted
to wrapper objects. The this value passed via a function call
(including calls made using Function.prototype.apply and
Function.prototype.call) do not coerce the passed this value to an
object (10.4.3, 11.1.1, 15.3.4.3, 15.3.4.4).
When you call
return Singleton();
The this value will actually be undefined. Try doing this.
return {
getInstance: function () {
return instance || new Singleton();
}
};
Not sure if this is the cause, but looks like the line that reads "tasks = $.Callbacks();" should be "this.tasks = $.Callbacks();". Also, as you are providing an instance as a callback, you will loose the 'this' binding. You function bodys that call anything with "this." should instead use a var that captures this in the outer closure (which looks like 'instance' does.
So for example, the method in question would read:
this.timer = setInterval(function()
{
instance.tasks.fire();
}, 1000);