How to return a JQuery promise - javascript

I have the following durendal code:
var variable = ko.observable("");
function activate(){
return myModel.doSomething(variable);
}
doSomething is defined as follow:
function doSomething(variable){
if(someCondition)
return $.ajax(...).success(function(data){variable(data)});
variable(defaultValue);
return defaultValue;
}
This code works fine when someCondition==true, variable gets initialized with the ajax call results.
when someCondition==false the activate method returns without the variable being initialized.
From what I read in the documentation, the happy path works because the ajax call returns a promise but not in the else case.
I tried this:
function activate(){
return myModel.doSomething(variable).promise();
}
but I get an error saying that the observable does not have a promise method which makes sense.
Question
How can I make the else clause return a promise just as what the ajax call does?

You should better encapsulate the logic inside the doSomething function, so it always returns a promise. You can use the system.defer from Durandal to achieve this:
var system = require('durandal/system');
function doSomething(){
return system.defer(function(dfd){
if (someCondition)
return $.ajax(...).then(function (res) {
dfd.resolve(res);
});
else
dfd.resolve(defaultValue);
}).promise();
}
And then in your activate:
function activate(){
return myModel.doSomething().then(function(res){
variable(res);
});
}
Notice that the variable to be updated is no loger passed to the function. The promise will always return a value, either from the AJAX call or the default one.

Related

Promise behavior and printing order

I'm a bit new to JavaScript and i can't figure out why the printing order of the following code is Noam and then Amit and only them.
can someone clarify it?
function rc(){
console.log('Assaf');
}
function thenCall(){
console.log('Amit');
}
function myPromise(){
return Promise.resolve(function(){
console.log('Yarden');
rc();
});
}
myPromise().then(function(){thenCall()});
console.log('Noam');
Promise.resolve takes a result which is passed to the then. It does not call the function. Notice the callback to .then.
function rc(){
console.log('Assaf');
}
function thenCall(){
console.log('Amit');
}
function myPromise(){
return Promise.resolve(function(){
console.log('Yarden');
rc();
});
}
myPromise().then(function(fn){
console.log(fn); // notice
thenCall()
});
console.log('Noam');
The function inside Promise.resolve should be executed as it will wait for a result to return:
function rc(){
console.log('Assaf');
}
function thenCall(){
console.log('Amit');
}
function myPromise(){
return Promise.resolve(function(){
console.log('Yarden');
rc();
}());
}
myPromise().then(function(){thenCall()});
console.log('Noam');
Let's divide the question by the people you're trying to log
Noam
Noam is printed first, because there is no async process in the code. You do use a Promise but it instantly executes the resolve method.
Amit
When Promise.resolve gets invoked, the function in the .then method will be executed. Therefore Amit gets printed in the console correctly.
Yarden and Assaf
Promise.resolve immediately resolves a new Promise and calls therefore the .then method immediately. The argument given to Promise.resolve is the value that will be passed as an argument to the .then method.
To create an actual Promise that either resolves or rejects based on some logic, this should get you started:
var a = true;
function myPromise() {
return new Promise(function (resolve, reject) {
console.log('first');
if (a) {
return setTimeout(function () {
resolve('value_to_pass_to_then');
}, 1000);
}
return reject('value_to_pass_to_catch');
});
}
myPromise()
.then(function (val) { console.log('second'); })
.catch(function (val) {})
The Yarden function is what the promise returned by myPromise() resolves to. But you aren't doing anything with the resolve value:
myPromise().then(function(/* resolve value would go here */){thenCall()});
The value a promise resolves to is the first argument of the function passed to .then. But you don't have an argument there. If you wanted to run that function, you would have to use it as an argument and then call it explicitly (which would in turn call the function that prints Assaf):
function rc() {
console.log('Assaf');
}
function thenCall() {
console.log('Amit');
}
function myPromise() {
return Promise.resolve(function() {
console.log('Yarden');
rc();
});
}
myPromise().then(function(yardenFn) {
thenCall();
yardenFn();
});
console.log('Noam');

Using a function that returns a promise, inside a then() block

I have a promise chain as follows :
return performTaskA().
then(performTaskB).
then(performTaskC).
then(performTaskD).
then(performTaskE);
performTaskD is as follows :
function performTaskD() {
Model.something().
then(function(result) {
something something with result; //BREAKPOINT 1
});
}
When I run the promise chain above, BREAKPOINT 1 never gets hit and the control proceeds to performTaskE.
However, when I call the function performTaskD() separately, BREAKPOINT 1 does get hit. What am I doing wrong in the case of the promise chain?
If I return the promise in performTaskD, I still have the same issue. The only difference is that the control never proceeds to performTaskE and the process exits.
For clarity, performTaskD is as follows :
AccountModel.findById(acctId).
then(function (account) {
destAccount = account; //destAccount is a var declared in the outer scope.
});
return the Promises
function performTaskD() {
return Model.something().
then(function(result) {
return something something with result; //BREAKPOINT 1
});
}
According to Mongoose documentation Model.find(something) does not return a promise. You need to call Model.find(something).exec(). The performTaskD should be something like:
function performTaskD(AcctId) {
return AccountModel.findById(AcctId).exec().
then(function (account) {
destAccount = account;
return account;
});
}
"then" function is called when a promise is resolved. In your task functions, you handle the promise inside the function itself, so the rest of the chain's "then" doesnt gets called.
Use Promise.resolve
function performTaskD() {
return Model.something().
then(function(result) {
something something with result; //BREAKPOINT 1
Promise.resolve(result)
});
}

Can someone explain the following code for me?

I'm trying to understand Promise. But here I'm confused.
I want to create a test function that will print 3000 after 3 second, then print 2000 after 2 second, then print 1000 after 1 second. Here is my code:
'use strict';
var Q = require('q');
function delayConsole(timeOut) {
var defer = Q.defer();
setTimeout(function(){
console.log(timeOut);
defer.resolve(2000);
},timeOut);
return defer.promise;
}
// This works
delayConsole(3000).then(function(){
return delayConsole(2000);
}).then(function(){
return delayConsole(1000);
});
// This doesn't work. Why?
delayConsole(3000).then(delayConsole(2000)).then(delayConsole(1000));
There, you call the function delayConsole immediately :
.then(delayConsole(2000))
That is : you don't pass the function but the result of the function call, you don't wait for the promises to be chained.
When you do
then(function(){
return delayConsole(2000);
})
then you pass a function, not the result of that function call. The function can be called when the previous element in the promise chain is solved.
I just thought I'd share that you can make this construction work which is sometimes easier to use:
promise.then(delayConsole(3000)).then(delayConsole(2000)).then(delayConsole(1000));
by changing delayConsole() to this:
function delayConsole(timeOut) {
return function() {
var defer = Q.defer();
setTimeout(function(){
console.log(timeOut);
defer.resolve(2000);
},timeOut);
return defer.promise;
}
}
This way, calling delayConsole() just captures the timeout argument and returns a function that can be called later by the promise .then handler. So, you are still passing a function reference to the .then() handler which lets the promise engine call the internal function sometime later rather than execute it now.

Return statement in promises

I'm writing a simple controller for a GET request in node.js that uses promises. The code works, but I'm not 100% sure why. I have a getStoriesHelper function that returns a promise, and after I get the result, it does return res.json(listOfStories);. This works, but I was under the impression that returns only breaks out of a single function scope (so I assume it should only break out of the anonymous function scope), and just wondering why it would break out of the getStories function as well?
//for instance, the return statement only breaks out of the anonymous function and not from dummy()
function dummy() {
_.map([1, 2, 3], function(element) {
return element * 2;
}
}
//node.js controller
exports.getStories = function(req, res) {
var studentId = req.params.studentId;
User.findOne({role: 'student', _id: studentId}).exec()
.then(function(student) {
var isNursery = student.status === 'attending class' && user.bookLevel === 'nursery';
getStoriesHelper(student, isNursery).then(function(listOfStories) {
//shouldn't return only break out of this inner function scope
return res.json(listOfStories)
});
});
};
//dummy async code
function getStoriesHelper() {
var deferred = Q.defer();
setTimeout(function() {
deferred.resolve(123)
}, 3000);
return deferred.promise;
}
Your code works because
findOne and getStoriesHelper are asynchronous and res still is in scope within them.
The statement return res.json(listOfStories) does two things. it writes the response to the output stream as well as returns the value returned by res.json() which is never used, so in fact it won't harm if you do not return, the function is about to return already.
getStories function is already ended the moment you called an async function so don't think that inner return has any effect outside that anonymous function.

Immediately return a resolved promise using AngularJS

I'm trying to get my head around promises in JavaScript (in particular AngularJS).
I have a function in a service, let's call it fooService, that checks if we've loaded some data. If it has, I just want it to return, and if we haven't, we need to load the data and return a promise:
this.update = function(data_loaded) {
if (data_loaded) return; // We've loaded the data, no need to update
var promise = Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something with the data here
}
return promise;
}
I have another function that then calls the update function of fooService like so:
fooService.update(data_loaded).then(function() {
// Do something here when update is finished
})
My issue here is that if we don't need to load the data in the update function, a promise isn't returned, so the .then() is not called in my other function. What should the approach be here - basically I want to return a resolved promise immediately from the update() function if we do not need to get data from the Restangular call?
As your promise use the same syntax as the JavaScript native one, you could use and return an already resolved JavaScript promise : Promise.resolve()
return(Promise.resolve("MyReturnValue"));
The current accepted answer is overly complicated, and abuses the deferred anti pattern. Here is a simpler approach:
this.update = function(data_loaded) {
if (data_loaded) return $q.when(data); // We've loaded the data, no need to update
return Restangular.all('someBase').customGet('foo/bar')
.then(function(data) {
// Do something with the data here
});
};
Or, even further:
this._updatep = null;
this.update = function(data_loaded) { // cached
this._updatep = this._updatep || Restangular.all('someBase') // process in
.customGet('foo/bar'); //.then(..
return this._updatep;
};
AngularJS's $q service will help you here. It is much like Kris Kowal's Q promise library.
When you have an async method that may return a promise or value use the $q.when method. It will take what ever is passed to it, be it a promise or a value and create a promise that will be resolved/rejected based on the promise passed, or resolved if a value is passed.
$q.when( fooService.update(data_loaded) ).then(function(data){
//data will either be the data returned or the data
//passed through from the promise
})
and then in your update function return the data instead of just returning
if (data_loaded) return data_loaded;
Similar to Elo's answer, you can return an already resolved promise using the async/await syntax:
this.update = async (data_loaded) => {
if (data_loaded)
return await null; // Instead of null, you could also return something else
// like a string "Resolved" or an object { status: 200 }
else
return await OtherPromise();
}
You could use the $q.defer() like this:
this.update = function (data_loaded) {
var deferred = $q.defer();
if (data_loaded) {
deferred.resolve(null); // put something that your callback will know the data is loaded or just put your loaded data here.
} else {
Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something here when update is finished
deferred.resolve(data);
}
}
return deferred.promise;
};
Hope this helps.

Categories

Resources