This question already has answers here:
Why does JQuery.getJSON() have a success and a done function?
(2 answers)
Closed 5 years ago.
I would like to know if there are any conceptual differences between these two codes:
Code 1:
$(function(){
var url = "url";
$.getJSON(url, function(data){
console.log(data);
})
});
Code 2:
$(function(){
var url = "url";
$.getJSON(url).done(function(data){
console.log(data);
})
});
In which situation the $.getJson().done() method is most relevant ?
The First one uses a callback function as a second param. This allows you to execute code after the function is completed. Note, you are in a separate function.
The Second also uses a callback function as a promise but it is working different under the hood.
// version one
setTimeout(function() {
doStuff1();
doStuff2();
}, 1000)
// version one - callback
function doStuff1() {
doSomething1("value", function(responce) {
console.log(responce);
});
};
function doSomething1(v, cb) {
if (typeof v === "string") {
cb(true);
} else {
cb(false);
}
return false;
}
// note the function will always return false but the callback gets the value you want
// version 2, class with promise callback
// look at the class function and see how it works slightly differently
function doStuff2() {
var $ = new doSomething2();
$.Something("value").done(function(resp) {
console.log(resp)
});
};
class doSomething2 {
constructor() {
this.v = false;
}
Something(val) {
if (typeof val === "string") {
this.v = true;
} else {
this.v = false;
}
return this;
}
done(cb) {
return cb(this.v);
}
}
i use angular.js in front side.
in my controller.js i defined an init() method that will be called in
init of my controller.
Init method definition:
var init = function () {
$scope.callTeamsService();
if ($scope.teams.length == 0){
....
}else{
...
}
.....
};
in $scope.callTeamsService i filled in $scope.teams variable.
$scope.callTeamsService method definition:
$scope.callTeamsService = function(){
NavService.getTeams(function (response) {
$timeout(function () {
$scope.teams = response;
}
}, 200);
});
};
In my service.js i defined a getTeams method as follow:
service.getEquipes = function (callback) {
$http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams')
.success(function (response) {
callback(response);
});
};
My problem is when $scope.teams.length == 0 condition is reached the
service.getEquipes method in my service.js is not yet called.
How can i modify this code in order to finish the execution of $scope.callTeamsService method before reaching $scope.teams.length == 0 condition.
service/factory
service.getEquipes = function () {
return $http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams');
};
// controller
var promise = NavService.getTeams.then (
function(data) {
//assign to $scope or do logic
},
function(err){
console.log(err)
}
)
How can i modify this code in order to finish the execution of $scope.callTeamsService method before reaching $scope.teams.length == 0 condition.
That's the wrong way round - you need to wait with executing the $scope.teams.length == 0 condition until the $scope.callTeamsService method has finished.
The classical method would be to give the $scope.callTeamsService method a callback parameter and call that in the timeout instead of $scope.teams = response;. Then you can put your condition in the init function in the callback that you pass.
However, you seem to want to use promises. For that, all of your functions (that all are asynchronous) should return a promise:
service.getEquipes = function (callback) {
return $http.get(urlBase+'users/' + $rootScope.globals.currentUser.loggedUser.idUser + '/teams');
}
(that was easy, $http already returns promises)
$scope.callTeamsService = function() {
return NavService.getTeams().then(function(teams) {
return $timeout(function() {
return teams;
}, 200);
});
};
(and $timeout does as well - by invoking then and returning it from the callback you can chain them and get a new promise for both)
function init() {
return $scope.callTeamsService().then(function(teams) {
$scope.teams = teams;
if (teams.length == 0) {
…
} else {
…
}
});
}
I can't get $.when.apply to evaluate any of the defined functions in array, what am I doing wrong here?
function Logic(address) {
this.address = address;
}
Logic.prototype.Get = function (pk, success, failure) {
var scope = this;
return $.ajax({
url: scope.address + '/Get',
type: "GET",
data: { 'pk': pk },
contentType: "application/json; charset=utf-8",
success: function (data) {
success(data.hasOwnProperty("d") ? data.d : data);
},
failure: function (ex) {
failure(ex);
}
});
};
function Screen(options) {
var scope = this;
if (options.pullings != null)
{
$.each(options.pullings , function (i, pulling)
{
scope.pullings.push(function () {
return pulling.logic.Get($('[name="' + pulling.pkField + '"]').val(),
function (row) {
$('#' + pulling.displayControlID).val(row[pulling.displayField]);
}, null);
});
});
}
}
Screen.prototype.Fill = function (pk) {
var scope = this;
$.when.apply($, scope.pullings).then(function () {
// none of the functions ever gets called and just enters this block
});
}
Because $.when() takes Promises or plain values. The function objects you pass in are considered to be values. Why did you expect them to be invoked automatically? You have to do that manually:
$.when.apply($, $.map(scope.pullings, function(fn) {
// calls every function
return fn();
})).then(function() {
// this block gets called when all results are available
});
Looks like a syntax error, change:
Screen.prototype.Fill = function (pk) {
var scope = this;
$.when.apply($, scope.pullings).then(function () {
// none of the functions ever gets called and just enters this block
}
}
to:
Screen.prototype.Fill = function (pk) {
var scope = this;
$.when.apply($, scope.pullings).then(function () {
// none of the functions ever gets called and just enters this block
});
}
That's my initial thinking, have you checked the console to see what errors you might be getting?
An overlooked alternative to $.when.apply is to accumulate when promises in the loop e.g. using the pattern promise = $.when(promise, anotherpromise)
e.g. something like
// Start with a resolved promise - which undefined represents!
var promise;
$.each(options.pullings, function (i, pulling) {
promise = $.when(promise, Get($('[name="' + pulling.pkField + '"]').val(),
function (row) {
$('#' + pulling.displayControlID).val(row[pulling.displayField]);
}, null);
});
promise.then(function(){
// called when all promises are completed
});
Given this code:
var something = function(callback) {
if(condition) {
Mongoose.findOne(id, function(err, doc) {
if(doc) {
callback(doc);
} else {
callback();
}
});
} else {
callback();
}
}
How would I rewrite it in a cleaner way so that 'callback' is just called in one place. I assume I can wrap this entire thing somehow and do that - I've seen it but cannot get it quite right.
Since you said there are complex steps to call the callback, try the below
var something = function(callback) {
var callCallback = function(doc){
//do all other things you want to do to call the callback
callback(doc);
};
if(condition) {
Mongoose.findOne(id, function(err, doc) {
if(doc) {
callCallback(doc);
} else {
callCallback();
}
});
} else {
callCallback();
}
}
var something = function (callback) {
var f = function (e, d) { callback(d) };
if (condition) {
Mongoose.findOne(id, f);
} else {
f();
}
}
my reasoning is that if d is false then we still can pass it on to callback and it will be the same almost as passing no arguments at all.
I've looked at many implementations and they all look so different I can't really distill what the essence of a promise is.
If I had to guess it is just a function that runs when a callback fires.
Can someone implement the most basic promise in a few lines of code w/ out chaining.
For example from this answer
Snippet 1
var a1 = getPromiseForAjaxResult(ressource1url);
a1.then(function(res) {
append(res);
return a2;
});
How does the function passed to then know when to run.
That is, how is it passed back to the callback code that ajax fires on completion.
Snippet 2
// generic ajax call with configuration information and callback function
ajax(config_info, function() {
// ajax completed, callback is firing.
});
How are these two snippets related?
Guess:
// how to implement this
(function () {
var publik = {};
_private;
publik.then = function(func){
_private = func;
};
publik.getPromise = function(func){
// ??
};
// ??
}())
Fundamentally, a promise is just an object that has a flag saying whether it's been settled, and a list of functions it maintains to notify if/when it is settled. Code can sometimes say more than words, so here's a very basic, not-real-world example purely indended to help communicate the concepts:
// See notes following the code for why this isn't real-world code
function Promise() {
this.settled = false;
this.settledValue = null;
this.callbacks = [];
}
Promise.prototype.then = function(f) {
if (this.settled) {
f(this.settledValue); // See notes 1 and 2
} else {
this.callbacks.push(f);
}
// See note 3 about `then`
// needing a return value
};
Promise.prototype.settle = function(value) { // See notes 4 and 5
var callback;
if (!this.settled) {
this.settled = true;
this.settledValue = value;
while (this.callbacks.length) {
callback = this.callbacks.pop();
callback(this.settledValue); // See notes 1 and 2
}
}
};
So the Promise holds the state, and the functions to call when the promise is settled. The act of settling the promise is usually external to the Promise object itself (although of course, that depends on the actual use, you might combine them — for instance, as with jQuery's ajax [jqXHR] objects).
Again, the above is purely conceptual and missing several important things that must be present in any real-world promises implementation for it to be useful:
then and settle should always call the callback asynchronously, even if the promise is already settled. then should because otherwise the caller has no idea whether the callback will be async. settle should because the callbacks shouldn't run until after settle has returned. (ES2015's promises do both of these things. jQuery's Deferred doesn't.)
then and settle should ensure that failure in the callback (e.g., an exception) is not propagated directly to the code calling then or settle. This is partially related to #1 above, and more so to #3 below.
then should return a new promise based on the result of calling the callback (then, or later). This is fairly fundamental to composing promise-ified operations, but would have complicated the above markedly. Any reasonable promises implementation does.
We need different types of "settle" operation: "resolve" (the underlying action succeeded) and "reject" (it failed). Some use cases might have more states, but resolved and rejected are the basic two. (ES2015's promises have resolve and reject.)
We might make settle (or the separate resolve and reject) private in some way, so that only the creator of the promise can settle it. (ES2015 promises — and several others — do this by having the Promise constructor accept a callback that receives resolve and reject as parameter values, so only code in that callback can resolve or reject [unless code in the callback makes them public in some way].)
Etc., etc.
Can someone implement the most basic promise in a few lines?
Here it is:
function Promise(exec) {
// takes a function as an argument that gets the fullfiller
var callbacks = [], result;
exec(function fulfill() {
if (result) return;
result = arguments;
for (let c;c=callbacks.shift();)
c.apply(null, arguments);
});
this.addCallback = function(c) {
if (result)
c.apply(null, result)
else
callbacks.push(c);
}
}
Additional then with chaining (which you will need for the answer):
Promise.prototype.then = function(fn) {
return new Promise(fulfill => {
this.addCallback((...args) => {
const result = fn(...args);
if (result instanceof Promise)
result.addCallback(fulfill);
else
fulfill(result);
});
});
};
How are these two snippets related?
ajax is called from the getPromiseForAjaxResult function:
function getPromiseForAjaxResult(ressource) {
return new Promise(function(callback) {
ajax({url:ressource}, callback);
});
}
I've implement one in ES7. With chaining, it's 70 lines, if that counts as few. I think State Machine is the right paradigm for implementing promises. Resulting code is more understandable than lots of ifs IMHO. Described fully in this article.
Here's the code:
const states = {
pending: 'Pending',
resolved: 'Resolved',
rejected: 'Rejected'
};
class Nancy {
constructor(executor) {
const tryCall = callback => Nancy.try(() => callback(this.value));
const laterCalls = [];
const callLater = getMember => callback => new Nancy(resolve => laterCalls.push(() => resolve(getMember()(callback))));
const members = {
[states.resolved]: {
state: states.resolved,
then: tryCall,
catch: _ => this
},
[states.rejected]: {
state: states.rejected,
then: _ => this,
catch: tryCall
},
[states.pending]: {
state: states.pending,
then: callLater(() => this.then),
catch: callLater(() => this.catch)
}
};
const changeState = state => Object.assign(this, members[state]);
const apply = (value, state) => {
if (this.state === states.pending) {
this.value = value;
changeState(state);
for (const laterCall of laterCalls) {
laterCall();
}
}
};
const getCallback = state => value => {
if (value instanceof Nancy && state === states.resolved) {
value.then(value => apply(value, states.resolved));
value.catch(value => apply(value, states.rejected));
} else {
apply(value, state);
}
};
const resolve = getCallback(states.resolved);
const reject = getCallback(states.rejected);
changeState(states.pending);
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
static resolve(value) {
return new Nancy(resolve => resolve(value));
}
static reject(value) {
return new Nancy((_, reject) => reject(value));
}
static try(callback) {
return new Nancy(resolve => resolve(callback()));
}
}
Here's a light-weight promise implementation, called 'sequence', which I use in my day-to-day work:
(function() {
sequence = (function() {
var chained = [];
var value;
var error;
var chain = function(func) {
chained.push(func);
return this;
};
var execute = function(index) {
var callback;
index = typeof index === "number" ? index : 0;
if ( index >= chained.length ) {
chained = [];
return true;
}
callback = chained[index];
callback({
resolve: function(_value) {
value = _value;
execute(++index);
},
reject: function(_error) {
error = _error;
execute(++index);
},
response: {
value: value,
error: error
}
});
};
return {
chain: chain,
execute: execute
};
})();
})();
Once initialized, you can use sequence in the following way:
sequence()
.chain(function(seq) {
setTimeout(function() {
console.log("func A");
seq.resolve();
}, 2000);
})
.chain(function(seq) {
setTimeout(function() {
console.log("func B");
}, 1000)
})
.execute()
To enable the actual chaining, you need to call the resolve() function of the seq object, which your callbacks must use as an argument.
Sequence exposes two public methods:
chain - this method simply pushes your callbacks to a private array
execute - this method uses recursion to enable the proper sequential execution of your callbacks. It basically executes your callbacks in the order you've chained them by passing the seq object to each of them. Once the current callback is resolved/rejected, the next callback is executed.
The 'execute' method is where the magic happens. It passes the 'seq' object to all of your callbacks. So when you call seq.resolve() or seq.reject() you'll actually call the next chained callback.
Please, note that this implementation stores a response from only the previously executed callback.
For more examples and documentation, please refer to:
https://github.com/nevendyulgerov/sequence
Here is a simple Promise implementation that works for me.
function Promise(callback) {
this._pending = [];
this.PENDING = "pending";
this.RESOLVED = "resolved";
this.REJECTED = "rejected";
this.PromiseState = this.PENDING;
this._catch = function (error) {
console.error(error);
};
setTimeout(function () {
try {
callback.call(this, this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}.bind(this), 0)
};
Promise.prototype.resolve = function (object) {
if (this.PromiseState !== this.PENDING) return;
while (this._pending.length > 0) {
var callbacks = this._pending.shift();
try {
var resolve = callbacks.resolve;
if (resolve instanceof Promise) {
resolve._pending = resolve._pending.concat(this._pending);
resolve._catch = this._catch;
resolve.resolve(object);
return resolve;
}
object = resolve.call(this, object);
if (object instanceof Promise) {
object._pending = object._pending.concat(this._pending);
object._catch = this._catch;
return object;
}
} catch (error) {
(callbacks.reject || this._catch).call(this, error);
return;
}
}
this.PromiseState = this.RESOLVED;
return object;
};
Promise.prototype.reject = function (error) {
if (this.PromiseState !== this.PENDING) return;
this.PromiseState = this.REJECTED;
try {
this._catch(error);
} catch (e) {
console.error(error, e);
}
};
Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = onFulfilled || function (result) {
return result;
};
this._catch = onRejected || this._catch;
this._pending.push({resolve: onFulfilled, reject: onRejected});
return this;
};
Promise.prototype.catch = function (onRejected) {
// var onFulfilled = function (result) {
// return result;
// };
this._catch = onRejected || this._catch;
// this._pending.push({resolve: onFulfilled, reject: onRejected});
return this;
};
Promise.all = function (array) {
return new Promise(function () {
var self = this;
var counter = 0;
var finishResult = [];
function success(item, index) {
counter++;
finishResult[index] = item;
if (counter >= array.length) {
self.resolve(finishResult);
}
}
for(var i in array) {
var item = array[i];
if (item instanceof Promise) {
item.then(function (result) {
success(result,this);
}.bind(i), function (error) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self._catch(error);
})
} else {
success(item, i);
}
}
});
};
Promise.race = function (array) {
return new Promise(function () {
var self = this;
var counter = 0;
var finishResult = [];
array.map(function (item) {
if (item instanceof Promise) {
item.then(function (result) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self.resolve(result);
}, function (error) {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self._catch(error);
})
} else {
array.map(function (item) {
item.PromiseState = Promise.REJECTED
});
self.resolve(item);
}
})
});
};
Promise.resolve = function (value) {
return new Promise(function (resolve, reject) {
try {
resolve(value);
} catch (error) {
reject(error);
}
});
};
Promise.reject = function (error) {
return new Promise(function (resolve, reject) {
reject(error);
});
}
Discussing here.
Fiddle: here.
here is the absolute minimum of a promise architecture
function Promise(F) {
var gotoNext = false;
var stack = [];
var args = [];
var isFunction = function(f) {
return f && {}.toString.call(f) === '[object Function]';
};
var getArguments = function(self, _args) {
var SLICE = Array.prototype.slice;
_args = SLICE.call(_args);
_args.push(self);
return _args;
};
var callNext = function() {
var method = stack.shift();
gotoNext = false;
if (isFunction(method)) method.apply(null, args);
};
var resolve = [(function loop() {
if (stack.length) setTimeout(loop, 0);
if (gotoNext) callNext();
})];
this.return = function() {
gotoNext = true;
args = getArguments(this, arguments);
if(resolve.length) resolve.shift()();
return this;
};
this.then = function(fn) {
if (isFunction(fn)) stack.push(fn);
return this;
};
return this.then(F).return();
}
// --- below is a working implementation --- //
var bar = function(p) {
setTimeout(function() {
console.log("1");
p.return(2);
}, 1000);
};
var foo = function(num, p) {
setTimeout(function() {
console.log(num);
p.return(++num);
}, 1000);
};
new Promise(bar)
.then(foo)
.then(foo)
.then(foo);