from
https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns
what promise is he talking about?
myApp.factory('Configurations', function (Restangular, MotorRestangular, $q) {
var getConfigurations = function () {
//Just return the promise we already have!
return MotorRestangular.all('Motors').getList().then(function (Motors) {
//Group by Cofig
var g = _.groupBy(Motors, 'configuration');
//Return the mapped array as the value of this promise
return _.map(g, function (m) {
return {
id: m[0].configuration,
configuration: m[0].configuration,
sizes: _.map(m, function (a) {
return a.sizeMm
})
}
});
});
};
return {
config: getConfigurations()
}
});
where is a promise? for me it looks more like it's an anti pattern to use his pattern. I cannot see any promise in this code apart from the word then nothing makes me think of a promise.
So what does return MotorRestangular... actually return?
One thing to remember is that, both the resolve and reject function return a promise. In other word, it's already promisified for you. Even if you don't explicitly return something from the resolve, what you get is a promise that has been resolved with a value of undefined.
In your example, MotorRestangular.all('Motors').getList() returns a promise and in the resolve function, the first param in then, another resolved promise is returned which will get the result of _.map function as input. It's like:
function YourCtrl(Configurations) {
Configurations.getConfiguration().then(function(resultOfMap) {
// use resultOfMap here
})
}
Note:
Don't get confused by resolved and onFulfilled. They are kinda the same but the former is for deferred object and the latter is from Promise specification.
MotorRestangular.all('Motors').getList()
is a promise returned by MotorRestangular. You can easily chain promises between methods whenever you just make the called function return a promise.
Related
My code is as follows: (node.js code)
'use strict';
var Promise = require('bluebird');
function promised()
{
return Promise.resolve();
}
function backgroundJob()
{
return Promise.resolve();
}
function doBackgroundJob()
{
// this is an intentional runaway promise.
backgroundJob()
.catch(function (err)
{
console.log('error', err);
});
}
function test()
{
return promised()
.then(function ()
{
doBackgroundJob();
return null; // without this, bluebird displays the warning
});
}
doBackgroundJob() does a background job, so it does not need to return a promise. But since it creates a promise, when the function is called in a then(), without an explicit return null in the then(), bluebird prints the following warning to the console. 'Warning: a promise was created in a handler but was not returned from it'.
This is somewhat unfair, since the caller does not need to know that the function uses a promise. How can I let bluebird to ignore the warning without return null in the then() in the caller?
I don't want to disable the warning, since it is quite useful.
One possibility is to add the background .then separately, and return only the base promise:
function test() {
const prom = promised();
prom.then(doBackgroundJob);
return prom;
}
while having doBackgroundJob return the promise (that proceeds to get discarded in this implementation):
function doBackgroundJob() {
// this is an intentional runaway promise.
return backgroundJob()
.catch(function(err) {
console.log('error', err);
});
}
allowing other consumers of doBackgroundJob to possibly use the promise it returns if needed.
It depends:
doBackgroundJob is doing something asynchronous, so it should return a promise as usual to let the caller know when it is finished. Even if it already does all error-handling and guarantees to only fulfill the promise. The caller, knowing that it returns a promise, would use return null in then callbacks to avoid the warning.
function doBackgroundJob() {
return backgroundJob().catch(console.error);
}
If the caller should not know what doBackgroundJobb is doing, you can create the promise asynchronously (so that Bluebird doesn't notice) and return nothing (so that the caller doesn't notice):
function doBackgroundJob() {
process.nextTick(() => {
// this is an intentional runaway promise.
backgroundJob().catch(console.error);
});
}
I'm using Node.js and q library.
I have a code that looks like this:
checkIfThingExists(function(idForAThing){
if(idForAThing){
updateThingData(idForAThing);
} else {
createThing(function(idForAThing){
updateThingData(idForAThing);
});
}
})
As you can see I need to call updateThingData(); twice.
Is there a way I can use promises to call updateThingData() just once, for example something like this? Of course this does not work and idForAThing is always undefined when the if statement runs:
checkIfThingExists(function(idForAThing){
if(!idForAThing){
createThing().then(function(newIdForAThing){
idForAThing = newIdForAThing
})
}
updateThingData(idForAThing);
})
Actually, what is happening is the .then method is a callback. It doesn't get called until after updateThingData is called.
You could instead create a promise chain that will resolve after it has the ID. Then chain the next .then callback to call updateThingData. Here is an example of that:
checkIfThingExists(function(idForAThing){
return Q.resolve() // or Promise.resolve if you were using the browser implementation
.then(function() {
if(!idForAThing) return createThing()
return idForAThing
})
.then(updateThingData)
})
You have to return in any case a promise.
So:
checkIfThingExists(function(idForAThing){
var promise
if(!idForAThing)
promise = createThing()
else
promise = Promise.resolve(idForAThing)
promise.then(function(id){
updateThingData(id)})
}
I saw promise implementation in Handling multiple catches in promise chain which produce a very readable chain
return validateInput
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
However, in order to do this each function needs to return a value instead of a Promise? Since Promise can resolves to either value or a Promise so this is not a problem. My goal is to turn every function to have readable clear logic as such.
The problem occurs when trying to unwind the nested promise function
return validateInput
.then(function(resultA) {
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Imagine the original implementation involves accessing the value from previous promise. With nested promise, it is easily achievable. But with flatten chain, I would need to break up each function like this
function validateInput = function (resultA ) {
return Promise.resolve({resultA : resultA, resultB :
}
function checkLoginPermission = function (mix ) {
let resultA = mix.resultA;
let resultB = mix.resultB
//Do something with resultA
...
}
This is worse when the last function in the chain rely on something from the very beginning. That means the value have to be passed down from the beginning of the chain even if it was not used.
So am I accidentally stepping on some kind of anti-pattern that might affect performance? How else can I achieve good readability without all these hassles?
This is actually where async and await come in. It's good when you need results across multiple asynchronous calls/promises to be in scope. If you can use that, I'd say try it.
async function foo () {
const input = await validateInput()
const hasPermission = await checkLoginPermission(input)
const result = await checkDisableUser(hasPermission)
return await changePassword(result)
}
Just pass the variables into what function as they need to be. Just showing an example there. I was also a bit unsure of how you're setting validateInput, i think you need to put await infront of the function call itself.
If you cannot use async/await, I usually go with your 2nd code snippet, or define the higher scope variables ontop:
let resultA
return validateInput
.then(function(result) {
resultA = result
return checkLoginPermission
.then (function(resultB) {
// Do something with resultA
})
});
Promises are pattern, related to functional programming, there direct passing data from one function to other is a basic (it's called compose, here examples: http://scott.sauyet.com/Javascript/Talk/Compose/2013-05-22/). So it's not anti-pattern by no means.
I can't see any problem in such pattern. You can pass any data you want to next Promises and in nested Promises grab what they need. It's preety transparent and clear:
function validateInput() {
return Promise.resolve({resultA: 1});
}
function checkLoginPermission(result) {
return new Promise(function(resolve, reject) {
// ...
// code
// ...
result.resultB = 2;
return resolve(result);
});
}
function checkDisableUser(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous function
let resultB = result.resultB;
// ...
// code
// ...
result.resultC = 3;
return resolve(result);
});
}
function changePassword(result) {
return new Promise(function(resolve, reject) {
// grab some data from previous functions
let resultB = result.resultB;
let resultC = result.resultC;
// ...
// code
// ...
result.resultD = resultB * resultC;
return resolve(result);
});
}
validateInput()
.then(checkLoginPermission)
.then(checkDisableUser)
.then(changePassword);
Also you can collect data in some variable, declared before Promises and so you will not have to pass result. But it will destroy functional nature of Promises.
The inner .then(/* ... */) callbacks can return either a primitive value or a Promise that resolves to some value. If it is another promise then the next .then won't start until the inner promise is resolved. Essentially, Promises always resolve to a non-promise type. If you resolve or return another Promise, it will be automatically unwrapped.
I would like to propose a solution using ramda.js#pipeP().
The good thing about this function is that it resolves promises sequentially.
We can rewrite your example using pipeP():
import pipeP from 'ramda/src/pipeP'
pipeP([
checkLoginPermission,
checkDisableUser,
changePassword
])(initialValue)
.then(responseChangePassword => { ... })
The results of a previous promise are passed to the following one.
(function() {
"use strict";
var storage = chrome.storage.sync;
var localStorage = null;
function getOutsideScope(property) {
if (localStorage.hasOwnProperty(property)) {
return localStorage[property];
}
}
function fulfill(data) {
return data;
}
function rejected(err) {
log(err);
}
window.app.storage = {
get: function(storageKey, storageProp) {
var promise = new Promise(function(fullfill, reject) {
chrome.storage.sync.get(storageKey, function(data) {
if (data.hasOwnProperty(storageKey)) {
fullfill(data[storageKey]);
} else {
reject(new Error(storageKey + " does not exist in the storage values."));
}
});
});
return promise.then(fulfill, rejected);
},
set: function(storageKey, storageItem) {
},
onChanged: function(fn) {
}
};
})();
So the above is my IIFE wrapper for chrome storage, and well the return is being a pain in the bleep. So I decided to give Promises a try, this is my first time so don't be too rough on me on this. Basically this is what I want to do
var property = app.storage.get("properties");
//property should equal "value"
//except it returns undefined
So adding the promises it does get the value except it returns this
Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "value"}
Am I doing something wrong with Promises I've tried reading HTML5Rocks, the MDN, and video tutorials except it doesn't really mention much of how to return a value. This does NOT work
get:function(storageKey,storageProp) {
chrome.storage.sync.get(storageKey,function(data) {
//for simplicity and no error checking -_-
return data[storageKey];
});
}
The function returns exactly what it is supposed to return: A promise. To get the value once the promise is fulfilled, you add a callback via .then:
app.storage.get("properties").then(function(property) {
// property will be "value"
});
From MDN:
A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers to an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of the final value, the asynchronous method returns a promise of having a value at some point in the future.
[...]
A pending promise can become either fulfilled with a value, or rejected with a reason (error). When either of these happens, the associated handlers queued up by a promise's then method are called.
I've a function, which returns a promise, inside that I call another function, and the status of this promise is based on the status of the inner promise.
Is there a way to shorthand this process. Look at the example below.
function foo(bar) {
var deferred = Q.defer();
switch (bar) {
case 'baz1':
deferred.resolve();
break;
case 'baz2':
deferred.reject();
break;
case 'this_is_how_i_do_it':
funReturningPromise().then(function (value) {
deferred.resolve(value);
}, function (err) {
deferred.reject(err);
});
break;
case 'can_we_do_it_like_this':
// can we do something like this, which will resolve or reject the deferred,
// based on the status promise returned by funReturningPromise().
// 'chain' is just a name
funReturningPromise().chain(deferred);
break;
}
return deferred;
}
Thanks,
It doesn't work as well for more complex functions, but in your example you could do something like:
case 'can_we_do_it_like_this':
return funReturningPromise();
You can also try adding your own promise.prototype.chain method if you're using only q promises.
If you return a value, throw an exception, or return a promise from inside any function managed by Q, particularly the callbacks given to then, the chained promise will “become” that value, exception, or promise.
var foo = Q.fbind(function (bar) {
switch (bar) {
case 'baz1':
return;
case 'baz2':
throw new Error("");
case 'this_is_how_you_can_do_it':
return funReturningPromise();
}
});
In this case, I’m using Q.fbind to guarantee that the return result is a promise, but if foo were called in the context of a promise handler, just like funReturningPromise, the fbind would be unnecessary. If you want to invoke a function immediately and get a promise for the result, you can also use fcall.
If you already have the deferred, then you can resolve it with another promise.
deferred.resolve(funReturningPromise())
Otherwise, what Kris said.