I have a constructor function which registers an event handler:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
However, I'm not able to access the data property of the created object inside the callback. It looks like this does not refer to the object that was created, but to another one.
I also tried to use an object method instead of an anonymous function:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
but it exhibits the same problems.
How can I access the correct object?
What you should know about this
this (aka "the context") is a special keyword inside each function and its value only depends on how the function was called, not how/when/where it was defined. It is not affected by lexical scopes like other variables (except for arrow functions, see below). Here are some examples:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
To learn more about this, have a look at the MDN documentation.
How to refer to the correct this
Use arrow functions
ECMAScript 6 introduced arrow functions, which can be thought of as lambda functions. They don't have their own this binding. Instead, this is looked up in scope just like a normal variable. That means you don't have to call .bind. That's not the only special behavior they have, please refer to the MDN documentation for more information.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
Don't use this
You actually don't want to access this in particular, but the object it refers to. That's why an easy solution is to simply create a new variable that also refers to that object. The variable can have any name, but common ones are self and that.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Since self is a normal variable, it obeys lexical scope rules and is accessible inside the callback. This also has the advantage that you can access the this value of the callback itself.
Explicitly set this of the callback - part 1
It might look like you have no control over the value of this because its value is set automatically, but that is actually not the case.
Every function has the method .bind [docs], which returns a new function with this bound to a value. The function has exactly the same behavior as the one you called .bind on, only that this was set by you. No matter how or when that function is called, this will always refer to the passed value.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
In this case, we are binding the callback's this to the value of MyConstructor's this.
Note: When a binding context for jQuery, use jQuery.proxy [docs] instead. The reason to do this is so that you don't need to store the reference to the function when unbinding an event callback. jQuery handles that internally.
Set this of the callback - part 2
Some functions/methods which accept callbacks also accept a value to which the callback's this should refer to. This is basically the same as binding it yourself, but the function/method does it for you. Array#map [docs] is such a method. Its signature is:
array.map(callback[, thisArg])
The first argument is the callback and the second argument is the value this should refer to. Here is a contrived example:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
Note: Whether or not you can pass a value for this is usually mentioned in the documentation of that function/method. For example, jQuery's $.ajax method [docs] describes an option called context:
This object will be made the context of all Ajax-related callbacks.
Common problem: Using object methods as callbacks/event handlers
Another common manifestation of this problem is when an object method is used as callback/event handler. Functions are first-class citizens in JavaScript and the term "method" is just a colloquial term for a function that is a value of an object property. But that function doesn't have a specific link to its "containing" object.
Consider the following example:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
The function this.method is assigned as click event handler, but if the document.body is clicked, the value logged will be undefined, because inside the event handler, this refers to the document.body, not the instance of Foo.
As already mentioned at the beginning, what this refers to depends on how the function is called, not how it is defined.
If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
The solution is the same as mentioned above: If available, use .bind to explicitly bind this to a specific value
document.body.onclick = this.method.bind(this);
or explicitly call the function as a "method" of the object, by using an anonymous function as callback / event handler and assign the object (this) to another variable:
var self = this;
document.body.onclick = function() {
self.method();
};
or use an arrow function:
document.body.onclick = () => this.method();
Here are several ways to access the parent context inside a child context -
You can use the bind() function.
Store a reference to context/this inside another variable (see the below example).
Use ES6 Arrow functions.
Alter the code, function design, and architecture - for this you should have command over design patterns in JavaScript.
1. Use the bind() function
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
If you are using Underscore.js - http://underscorejs.org/#bind
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
2. Store a reference to context/this inside another variable
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
3. Arrow function
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
It's all in the "magic" syntax of calling a method:
object.property();
When you get the property from the object and call it in one go, the object will be the context for the method. If you call the same method, but in separate steps, the context is the global scope (window) instead:
var f = object.property;
f();
When you get the reference of a method, it's no longer attached to the object. It's just a reference to a plain function. The same happens when you get the reference to use as a callback:
this.saveNextLevelData(this.setAll);
That's where you would bind the context to the function:
this.saveNextLevelData(this.setAll.bind(this));
If you are using jQuery you should use the $.proxy method instead, as bind is not supported in all browsers:
this.saveNextLevelData($.proxy(this.setAll, this));
You should know about "this" Keyword.
As per my view you can implement "this" in three ways
(Self|Arrow function|Bind Method)
A function's this keyword behaves a little differently in JavaScript compared to other languages.
It also has some differences between strict mode and non-strict mode.
In most cases, the value of this is determined by how a function is called.
It can't be set by assignment during execution, and it may be different each time the function is called.
ES5 introduced the bind() method to set the value of a function's this regardless of how it's called,
And ES2015 introduced arrow functions that don't provide their own this binding (it retains this value of the enclosing lexical context).
Method1: Self - Self is being used to maintain a reference to the original this even as the context is changing. It's a technique often used in event handlers (especially in closures).
Reference: this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
Method2: Arrow function - An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords.
Arrow function expressions are ill-suited as methods, and they cannot be used as constructors.
Reference: Arrow function expressions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Method 3: Bind - The bind() method creates a new function that, when called, has its this keyword set to the provided value with a given sequence of arguments preceding any provided when the new function is called.
Reference: Function.prototype.bind()
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
The trouble with "context"
The term "context" is sometimes used to refer to the object referenced by this. Its use is inappropriate, because it doesn't fit either semantically or technically with ECMAScript's this.
"Context" means the circumstances surrounding something that adds meaning, or some preceding and following information that gives extra meaning. The term "context" is used in ECMAScript to refer to execution context, which is all the parameters, scope, and this within the scope of some executing code.
This is shown in ECMA-262 section 10.4.2:
Set the ThisBinding to the same value as the ThisBinding of the
calling execution context
Which clearly indicates that this is part of an execution context.
An execution context provides the surrounding information that adds meaning to the code that is being executed. It includes much more information than just the thisBinding.
The value of this isn't "context". It's just one part of an execution context. It's essentially a local variable that can be set by the call to any object and in strict mode, to any value at all.
First, you need to have a clear understanding of scope and behaviour of the this keyword in the context of scope.
this & scope:
There are two types of scope in JavaScript. They are:
Global Scope
Function Scope
In short, global scope refers to the window object. Variables declared in a global scope are accessible from anywhere.
On the other hand, function scope resides inside of a function. A variable declared inside a function cannot be accessed from the outside world normally.
The this keyword in the global scope refers to the window object. this inside a function also refers to the window object. So this will always refer to the window until we find a way to manipulate this to indicate a context of our own choosing.
--------------------------------------------------------------------------------
- -
- Global Scope -
- (globally "this" refers to window object) -
- -
- function outer_function(callback){ -
- -
- // Outer function scope -
- // Inside the outer function, the "this" keyword -
- // refers to window object -
- callback() // "this" inside callback also refers to the window object -
- } -
- -
- function callback_function(){ -
- -
- // Function to be passed as callback -
- -
- // Here "THIS" refers to the window object also -
- } -
- -
- outer_function(callback_function) -
- // Invoke with callback -
- -
--------------------------------------------------------------------------------
Different ways to manipulate this inside callback functions:
Here I have a constructor function called Person. It has a property called name and four method called sayNameVersion1, sayNameVersion2, sayNameVersion3, and sayNameVersion4. All four of them has one specific task. Accept a callback and invoke it. The callback has a specific task which is to log the name property of an instance of Person constructor function.
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// Function to be used as callback
var parentObject = this
console.log(parentObject)
}
Now let's create an instance from person constructor and invoke different versions of sayNameVersionX (X refers to 1,2,3,4) method with niceCallback to see how many ways we can manipulate the this inside callback to refer to the person instance.
var p1 = new Person('zami') // Create an instance of Person constructor
bind:
What bind do is to create a new function with the this keyword set to the provided value.
sayNameVersion1 and sayNameVersion2 use bind to manipulate this of the callback function.
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
The first one binds this with a callback inside the method itself. And for the second one, the callback is passed with the object bound to it.
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
call:
The first argument of the call method is used as this inside the function that is invoked with call attached to it.
sayNameVersion3 uses call to manipulate the this to refer to the person object that we created, instead of the window object.
this.sayNameVersion3 = function(callback){
callback.call(this)
}
And it is called like the following:
p1.sayNameVersion3(niceCallback)
apply:
Similar to call, the first argument of apply refers to the object that will be indicated by the this keyword.
sayNameVersion4 uses apply to manipulate this to refer to a person object
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
And it is called like the following. Simply the callback is passed,
p1.sayNameVersion4(niceCallback)
We can not bind this to setTimeout(), as it always executes with the global object (Window). If you want to access the this context in the callback function then by using bind() to the callback function, we can achieve it as:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
The question revolves around how the this keyword behaves in JavaScript. this behaves differently as below,
The value of this is usually determined by a function execution context.
In the global scope, this refers to the global object (the window object).
If strict mode is enabled for any function then the value of this will be undefined as in strict mode, global object refers to undefined in place of the window object.
The object that is standing before the dot is what the this keyword will be bound to.
We can set the value of this explicitly with call(), bind(), and apply()
When the new keyword is used (a constructor), this is bound to the new object being created.
Arrow functions don’t bind this — instead, this is bound lexically (i.e., based on the original context)
As most of the answers suggest, we can use the arrow function or bind() Method or Self var. I would quote a point about lambdas (arrow function) from Google JavaScript Style Guide
Prefer using arrow functions over f.bind(this), and especially over
goog.bind(f, this). Avoid writing const self = this. Arrow functions
are particularly useful for callbacks, which sometimes pass unexpectedly
additional arguments.
Google clearly recommends using lambdas rather than bind or const self = this
So the best solution would be to use lambdas as below,
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
References:
https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
arrow-functions-vs-bind
Currently there is another approach possible if classes are used in code.
With support of class fields, it's possible to make it the following way:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // This refers to the correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
For sure under the hood it's all the old good arrow function that binds context, but in this form it looks much more clear that explicit binding.
Since it's a Stage 3 Proposal, you will need Babel and appropriate Babel plugin to process it as for now (08/2018).
I was facing a problem with Ngx line chart xAxisTickFormatting function which was called from HTML like this: [xAxisTickFormatting]="xFormat".
I was unable to access my component's variable from the function declared. This solution helped me to resolve the issue to find the correct this.
Instead of using the function like this:
xFormat (value): string {
return value.toString() + this.oneComponentVariable; //gives wrong result
}
Use this:
xFormat = (value) => {
// console.log(this);
// now you have access to your component variables
return value + this.oneComponentVariable
}
Another approach, which is the standard way since DOM2 to bind this within the event listener, that let you always remove the listener (among other benefits), is the handleEvent(evt) method from the EventListener interface:
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
Detailed information about using handleEvent can be found here: DOM handleEvent: a cross-platform standard since year 2000
Some other people have touched on how to use the .bind() method, but specifically here is how you can use it with .then() if anyone is having trouble getting them to work together:
someFunction()
.then(function(response) {
//'this' wasn't accessible here before but now it is
}.bind(this))
As mentioned in the comments, an alternative would be to use an arrow function that doesn't have its own 'this' value
someFunction()
.then((response)=>{
//'this' was always accessible here
})
this in JavaScript:
The value of this in JavaScript is 100% determined by how a function is called, and not how it is defined. We can relatively easily find the value of this by the 'left of the dot rule':
When the function is created using the function keyword the value of this is the object left of the dot of the function which is called
If there is no object left of the dot then the value of this inside a function is often the global object (global in Node.js and window in a browser). I wouldn't recommend using the this keyword here because it is less explicit than using something like window!
There exist certain constructs like arrow functions and functions created using the Function.prototype.bind() a function that can fix the value of this. These are exceptions of the rule, but they are really helpful to fix the value of this.
Example in Node.js
module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);
const obj1 = {
data: "obj1 data",
met1: function () {
console.log(this.data);
},
met2: () => {
console.log(this.data);
},
};
const obj2 = {
data: "obj2 data",
test1: function () {
console.log(this.data);
},
test2: function () {
console.log(this.data);
}.bind(obj1),
test3: obj1.met1,
test4: obj1.met2,
};
obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);
Output:
Let me walk you through the outputs one by one (ignoring the first log starting from the second):
this is obj2 because of the left of the dot rule, we can see how test1 is called obj2.test1();. obj2 is left of the dot and thus the this value.
Even though obj2 is left of the dot, test2 is bound to obj1 via the bind() method. The this value is obj1.
obj2 is left of the dot from the function which is called: obj2.test3(). Therefore obj2 will be the value of this.
In this case: obj2.test4() obj2 is left of the dot. However, arrow functions don't have their own this binding. Therefore it will bind to the this value of the outer scope which is the module.exports an object which was logged in the beginning.
We can also specify the value of this by using the call function. Here we can pass in the desired this value as an argument, which is obj2 in this case.
I am trying to make some modifications to a Node.js app that I forked, what I am trying to do is call a function within another one.
I have attempted a crack at this by simply calling the method as follows but I'm not really that familiar with Node.js so I'm not sure I'm doing it right.
'use strict';
/*
* initalize the class
*/
function MessageBot(bot, user, cache) {
this.bot = bot;
this.user = user;
this.cache = cache;
}
/*
* perform commands
*/
MessageBot.prototype.librarySearch = function(searchText) {
var self = this;
// call SOS function - this is the bit that doesn't work
MessageBot.prototype.callSos(somenumbervar);
}
MessageBot.prototype.callSos = function(number) {
// do something else here
var self = this;
var commandList = self.cache.get('commandList');
}
Remember that this ostensibly inherits the prototype (directly or indirectly). Therefore, you can just do this.callSos(somenumbervar).
If you want to reach the method through the prototype, you have to tell it what this is. With your current code, this in callSos() will be MessageBot.prototype -- certainly not what you want. So you can also do MessageBot.prototype.callSos.call(this, somenumbervar).
Which approach to take depends on how dynamic you want your objects to be. For example, if you want consumers of MessageBot to be able to "override" callSos() by installing their own method, then you should take the first approach (this.callSos()) as it will look up callSos in the object's inheritance chain. This process will only reach the method you've installed on the prototype if the method hasn't been overridden. IMO this is the approach you should take unless you have a very good reason not to.
See this example, which demonstrates how the two approaches differ regarding overriding, while also showing that both work with regards to passing the correct this value (since both methods can retrieve the expected value from this.data):
function ClassA(data) {
this.data = data;
}
ClassA.prototype.foo = function () {
console.log("this.bar() returned: " + this.bar());
console.log("ClassA.prototype.bar.call(this) returned: " + ClassA.prototype.bar.call(this));
};
ClassA.prototype.bar = function () {
return 'in the ClassA prototype, our data is ' + this.data;
};
console.log('--obj1-- (no override)');
var obj1 = new ClassA(3.14);
obj1.foo();
console.log('--obj2-- (override)');
var obj2 = new ClassA(42);
obj2.bar = function () {
return 'in an overriding method, our data is ' + this.data;
};
obj2.foo();
I need to use bluebird in my code and I have no idea how to use it. My code contains nested loops. When the user logs in, my code will run. It will begin to look for any files under the user, and if there are files then, it will loop through to get the name of the files, since the name is stored in a dictionary. Once it got the name, it will store the name in an array. Once all the names are stored, it will be passed along in res.render().
Here is my code:
router.post('/login', function(req, res){
var username = req.body.username;
var password = req.body.password;
Parse.User.logIn(username, password, {
success: function(user){
var Files = Parse.Object.extend("File");
var object = [];
var query = new Parse.Query(Files);
query.equalTo("user", Parse.User.current());
var temp;
query.find({
success:function(results){
for(var i=0; i< results.length; i++){
var file = results[i].toJSON();
for(var k in file){
if (k ==="javaFile"){
for(var t in file[k]){
if (t === "name"){
temp = file[k][t];
var getname = temp.split("-").pop();
object[i] = getname;
}
}
}
}
}
}
});
console.log(object);
res.render('filename', {title: 'File Name', FIles: object});
console.log(object);
},
error: function(user, error) {
console.log("Invalid username/password");
res.render('logins');
}
})
});
EDIT:The code doesn't work, because on the first and second console.log(object), I get an empty array. I am suppose to get one item in that array, because I have one file saved
JavaScript code is all parsed from top to bottom, but it doesn't necessarily execute in that order with asynchronous code. The problem is that you have the log statements inside of the success callback of your login function, but it's NOT inside of the query's success callback.
You have a few options:
Move the console.log statements inside of the inner success callback so that while they may be parsed at load time, they do not execute until both callbacks have been invoked.
Promisify functions that traditionally rely on and invoke callback functions, and hang then handlers off of the returned value to chain the promises together.
The first option is not using promises at all, but relying solely on callbacks. To flatten your code you will want to promisify the functions and then chain them.
I'm not familiar with the syntax you're using there with the success and error callbacks, nor am I familiar with Parse. Typically you would do something like:
query.find(someArgsHere, function(success, err) {
});
But then you would have to nest another callback inside of that, and another callback inside of that. To "flatten" the pyramid, we make the function return a promise instead, and then we can chain the promises. Assuming that Parse.User.logIn is a callback-style function (as is Parse.Query.find), you might do something like:
var Promise = require('bluebird');
var login = Promise.promisify(Parse.User.logIn);
var find = Promise.promisify(Parse.Query.find);
var outerOutput = [];
return login(yourArgsHere)
.then(function(user) {
return find(user.someValue);
})
.then(function(results) {
var innerOutput = [];
// do something with innerOutput or outerOutput and render it
});
This should look familiar to synchronous code that you might be used to, except instead of saving the returned value into a variable and then passing that variable to your next function call, you use "then" handlers to chain the promises together. You could either create the entire output variable inside of the second then handler, or you can declare the variable output prior to even starting this promise chain, and then it will be in scope for all of those functions. I have shown you both options above, but obviously you don't need to define both of those variables and assign them values. Just pick the option that suits your needs.
You can also use Bluebird's promisifyAll() function to wrap an entire library with equivalent promise-returning functions. They will all have the same name of the functions in the library suffixed with Async. So assuming the Parse library contains callback-style functions named someFunctionName() and someOtherFunc() you could do this:
var Parse = Promise.promisifyAll(require("Parse"));
var promiseyFunction = function() {
return Parse.someFunctionNameAsync()
.then(function(result) {
return Parse.someOtherFuncAsync(result.someProperty);
})
.then(function(otherFuncResult) {
var something;
// do stuff to assign a value to something
return something;
});
}
I have a few pointers. ... Btw tho, are you trying to use Parse's Promises?
You can get rid of those inner nested loops and a few other changes:
Use some syntax like this to be more elegant:
/// You could use a map function like this to get the files into an array of just thier names
var fileNames = matchedFiles.map(function _getJavaFile(item) {
return item && item.javaFile && item.javaFile.name // NOT NULL
&& item.javaFile.name.split('-')[0]; // RETURN first part of name
});
// Example to filter/retrieve only valid file objs (with dashes in name)
var matchedFiles = results.filter(function _hasJavaFile(item) {
return item && item.javaFile && item.javaFile.name // NOT NULL
&& item.javaFile.name.indexOf('-') > -1; // and has a dash
});
And here is an example on using Parse's native promises (add code above to line 4/5 below, note the 'then()' function, that's effectively now your 'callback' handler):
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.select("score", "playerName");
query.find().then(function(results) {
// each of results will only have the selected fields available.
});
I have seen Chaining an arbitrary number of promises in Q ; my question is different.
How can I make a variable number of calls, each of which returns asynchronously, in order?
The scenario is a set of HTTP requests, the number and type of which is determined by the results of the first HTTP request.
I'd like to do this simply.
I have also seen this answer which suggests something like this:
var q = require('q'),
itemsToProcess = ["one", "two", "three", "four", "five"];
function getDeferredResult(prevResult) {
return (function (someResult) {
var deferred = q.defer();
// any async function (setTimeout for now will do, $.ajax() later)
setTimeout(function () {
var nextResult = (someResult || "Initial_Blank_Value ") + ".." + itemsToProcess[0];
itemsToProcess = itemsToProcess.splice(1);
console.log("tick", nextResult, "Array:", itemsToProcess);
deferred.resolve(nextResult);
}, 600);
return deferred.promise;
}(prevResult));
}
var chain = q.resolve("start");
for (var i = itemsToProcess.length; i > 0; i--) {
chain = chain.then(getDeferredResult);
}
...but it seems awkward to loop through the itemsToProcess in that way. Or to define a new function called "loop" that abstracts the recursion. What's a better way?
There's a nice clean way to to this with [].reduce.
var chain = itemsToProcess.reduce(function (previous, item) {
return previous.then(function (previousValue) {
// do what you want with previous value
// return your async operation
return Q.delay(100);
})
}, Q.resolve(/* set the first "previousValue" here */));
chain.then(function (lastResult) {
// ...
});
reduce iterates through the array, passing in the returned value of the previous iteration. In this case you're returning promises, and so each time you are chaining a then. You provide an initial promise (as you did with q.resolve("start")) to kick things off.
At first it can take a while to wrap your head around what's going on here but if you take a moment to work through it then it's an easy pattern to use anywhere, without having to set up any machinery.
I like this way better:
var q = require('q'),
itemsToProcess = ["one", "two", "three", "four", "five"];
function getDeferredResult(a) {
return (function (items) {
var deferred;
// end
if (items.length === 0) {
return q.resolve(true);
}
deferred = q.defer();
// any async function (setTimeout for now will do, $.ajax() later)
setTimeout(function () {
var a = items[0];
console.log(a);
// pop one item off the array of workitems
deferred.resolve(items.splice(1));
}, 600);
return deferred.promise.then(getDeferredResult);
}(a));
}
q.resolve(itemsToProcess)
.then(getDeferredResult);
The key here is to call .then() on the deferred.promise with a spliced version of the array of workitems. This then gets run after the initial deferred promise resolves, which is in the fn for the setTimeout. In a more realistic scenario, the deferred promise would get resolved in the http client callback.
The initial q.resolve(itemsToProcess) kicks things off by passing in the work items to the first call of the work fn.
I added this in hopes it would help others.
Here is a concept of a state machine defined with Q.
Suppose you have the HTTP function defined, so it returns a Q promise object:
var Q_http = function (url, options) {
return Q.when($.ajax(url, options));
}
You can define a recursive function nextState as following:
var states = [...]; // an array of states in the system.
// this is a state machine to control what url to get data from
// at the current state
function nextState(current) {
if (is_terminal_state(current))
return Q(true);
return Q_http(current.url, current.data).then(function (result) {
var next = process(current, result);
return nextState(next);
});
}
Where function process(current, result) is a function to find out what the next step would be according to the current state and the result from the HTTP call.
When you use it, use it like:
nextState(initial).then(function () {
// all requests are successful.
}, function (reason) {
// for some unexpected reason the request sequence fails in the middle.
});
I propose another solutions, which looks easier to understand to me.
You do the same as you would when chaining promises directly:
promise.then(doSomethingFunction).then(doAnotherThingFunction);
If we put that into a loop, we get this:
var chain = Q.when();
for(...) {
chain = chain.then(functionToCall.bind(this, arg1, arg2));
};
chain.then(function() {
console.log("whole chain resolved");
});
var functionToCall = function(arg1, arg2, resultFromPreviousPromise) {
}
We use function currying to use multiple arguments. In our example
functionToCall.bind(this, arg1, arg2) will return a function with one argument: functionToCall(resultFromPreviousPromise)
You do not need to use the result from the previous promise.