ES2015 equivalent of $.Deferred() - javascript

I'm using Babel for a project, and I'm stuck with a very basic problem. I'm very used to jQuery's Deferred objects and I'm struggling to find its ES2015 equivalent, here is what I basically want:
// file1.js
let dfd = new Promise()
function functionCalledAtSomePoint(thing) {
dfd.resolve(thing)
}
export default { dfd }
// file2.js
import { dfd } from './file1'
dfd.then((thing) => {
console.log('Yay thing:', thing)
})
What should be the correct way to write this simple deferred?
EDIT with royhowie's answer:
// file1.js
let thing
function getThing(_thing) {
return new Promise((resolve) => {
if (el) {
thing = new Thing(el)
}
resolve(thing)
})
}
function functionCalledAtSomePoint(el) {
getThing(el)
}
export default { getThing }
// file2.js
import { getThing } from './file1'
getThing.then((thing) => {
console.log('Yay thing:', thing)
})

You can export the promise directly (instead of a function)—like you have—but then you'll only be able to use it (.then) once, which is probably not what you want.
Instead, you should export a function which returns a Promise:
file 1.js
import User from '../models/user'
export function getUsersFromDatabase () {
return new Promise((resolve, reject) => {
User.find({}, (err, users) => {
return err ? reject(err) : resolve(users)
})
})
}
file2.js
import { getUsersFromDatabase } from './file1'
getUsersFromDatabase().then((users) => {
// success
}).catch((err) => {
// no users
})
You can use the default Promise implementation, but it much slower than 3rd party modules, e.g., bluebird (which I very much recommend using).

I'm very used to jQuery's Deferred objects and I'm struggling to find its ES2015 equivalent
If you must use deferred, this should work
function makeDeferred() {
var res, rej;
let dfd = new Promise(function(resolve, reject) {
res = resolve;
rej = reject;
});
dfd.resolve = res;
dfd.reject = rej;
return dfd;
}
let dfd = makeDeferred();
However, rewriting your code to avoid such kludge would be preferable (but not unavoidable - I still have one piece of code I can't get rid of the deferred promise in, so I feel your pain

This class will allow you to use the regular Promise methods as well as an additional resolve(value) method. This should give you a similar functionality as jQuery.deferred().
function DeferredPromise() {
var _resolve = null;
var _reject = null;
this.promise = new Promise(function(resolve, reject) {
_resolve = resolve;
_reject = reject;
});
this.then = function() {
return this.promise.then(...arguments);
}
this.catch = function() {
return this.promise.catch(...arguments);
}
this.resolve = function() {
_resolve(...arguments);
}
this.reject = function() {
_reject(...arguments);
}
}
Then you can use it to create a new DeferredPromise:
var p = new DeferredPromise();
Wait for it:
p.then(val => {
console.log('val(1)', val);
})
Maybe wait for it another time, you can also chain it with a regular Promise:
p.then(val => {
console.log('val(2)', val);
return 42;
}).then(val => {
console.log('.then(somethingElse)', val);
})
.catch(err => { console.error('err', err); })
And resolve it whenever you want:
p.resolve({ username: 'Luke.Skywalker', age: 42 });

Related

Calling other functions of a class inside a Promise in Node

So, I have two methods in a class. Both returns a promise. The second function calls the first function from inside of the promise it returns.
module.exports = {
funcA: () => {
return new Promise((resolve, reject) => {
something ? resolve(something): reject('nope');
});
}
funcB: () => {
return new Promise(async(resolve, reject) => {
try {
const something = await this.funcA();
} catch(err) {
reject('error');
}
}
}
When I am trying to call funcB() from another class, like this:
let something = await someService.funcB();
I am getting:
TypeError: this.funcA() is not a function
Can you shed some light on why this is happening and how to solve this problem?
one way to make it work is to create the function outside of the module.exports block to get a reference of each function. Then this keyword can be omitted
const funcA = () => {
return new Promise((resolve, reject) => {
// code here
});
};
const funcB = () => {
return new Promise(async(resolve, reject) => {
try {
const something = await funcA();
resolve(something);
} catch(err) {
reject('error');
}
})
};
module.exports = {
funcA,
funcB
}
I think this is what you need to do
module.exports = {
funcA: function() {
return new Promise((resolve, reject) => {
something ? resolve(something): reject('nope');
});
}
funcB: function() {
return new Promise(async(resolve, reject) => {
try {
const something = await this.funcA();
} catch(err) {
reject('error');
}
}
}
I've found using arrow functions inside objects as you've done breaks this, but you can fix it this way.

How to write a simple mock of an API call that returns async

There is this sample application that has a level db Node JS library, but my server doesn't currently have level db installed and I don't need it.
How can I write a simple class that would just mock the API calls.
The store variable is the library I want to mock, and it has 2 API calls:
store.put
store.get
It has 2 API calls:
const store = level('./data/dbname123', { valueEncoding: 'json' });
save() {
debug(`saving id: ${this.id}`);
const properties = attributes.reduce((props, attr) => {
props[attr] = this[attr];
return props;
}, { fields: this.fields });
return new Promise((resolve, reject) => {
store.put(this.id, properties, (error) => {
if (error) { return reject(error); }
resolve(this);
});
});
}
static find(id) {
debug(`fetching id: ${id}`)
return new Promise((resolve, reject) => {
store.get(id, (error, properties) => {
if (error) { return reject(error); }
resolve(new Ticket(properties));
});
});
}
How can I quickly just mock those? I'm not too familiar with this style of JavaScript but it because of the promise wrapper I'm not sure if that is async call or?
You can just create an object with put and get methods on it that simulate what those methods do. Just make sure your functions follow the expected conventions — for example calling the callback with an error as the first argument if there's a problem.
Obviously this can be more involved and there are tools like Sinon that can help if you have to mock existing functions.
For example:
// simple mocks for store.get and store.put
let store = {
put(id, properties, fn){
// add whatever behavior you need and call callback fn
fn(null) // calling with null indicates no error.
},
get(id, fn){
// make some properties
let props = {
someProperties: "Hello",
id: id
}
// call callback
fn(null, props)
}
}
function save() {
return new Promise((resolve, reject) => {
store.put('id', 'properties', (error) => {
if (error) { return reject(error); }
resolve();
});
});
}
function find(id) {
return new Promise((resolve, reject) => {
store.get(id, (error, properties) => {
if (error) { return reject(error); }
resolve(properties);
});
});
}
// try them out
find("21")
.then(console.log)
save()
.then(() => console.log("done"))
Maybe my answer doesn't correspond to your question but to mock your library you can create your own storage
const store = function () {
var data = {};
return {
put: function(id, props, fn) {
data[id] = props;
fn(null);
},
get: function(id, fn) {
fn(null, data[id]);
}
}
}();
If you define your storage like it, you mock your library

Call a Q promise function after promise chain invoked

I am working in a Node.js app with Q promise library. I have two set of promise chains and one is for controlling the flow and one for calling service methods where I retrieve data from, My question is, I need to get the return value of the promise chain to my other promise chain.
MyExample.js
bookService.getBookById(bookId)
.then(bookDetals)
.then(function(returnValue) { // <- (A)
res.send(200, returnValue); // <- (C)
return returnValue;
}).catch(function(error) {
logger.error('Error getting values');
res.send(500, error);
});
bookDetals = function(book) {
myService.retrieveATypeData(book, bookId)
.then(function(bookData) {
myService.retrieveBTypeData(bookId)
.then(function(bdata) {
bookData[bTypeData] = bdata;
myService.retrieveCTypeData(bookId)
.then(function(cdata) {
bookData[cTypeData] = cdata;
}).done(function() {
return bookData; // <- (B)
})
});
});
};
In the above code, I am calling bookService.getBookById(bookId) and getting the book. Then I am calling bookDetals function which is a promise chain. But my problem is it returns the returnValue before the promise chains over. How can I get the return value of promise chain (in line (B)) to return in place (C). Currently it return before. so in place C it says undefined.
Since you are using Node, I would move towards ES6 Promises. If your current version does not yet support ES6 Promises, I would recommend you switch over to a library (es6-promise) that polyfills it for you. With ES6, you could do something like this:
// mock async promise
const getThing = id => (
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id
});
}, 250);
})
);
// mock async promise
const getDetailsA = thing => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign({}, thing, {
a: 'purple'
}));
}, 250);
})
};
// mock async promise
const getDetailsB = thing => (
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign({}, thing, {
b: 'monkey'
}));
}, 250);
})
);
// mock async promise
const getDetailsC = thing => (
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign({}, thing, {
c: 'dishwasher'
}));
}, 250);
})
);
getThing('123')
.then(getDetailsA)
.then(getDetailsB)
.then(getDetailsC)
.then(console.log)
.catch(console.error);
You need to return a promise:
bookDetals = function(book) {
return Q.Promise(function(resolve, reject, notify) {
myService.retrieveATypeData(book, bookId)
.then(function(bookData) {
myService.retrieveBTypeData(bookId)
.then(function(bdata) {
bookData[bTypeData] = bdata;
myService.retrieveCTypeData(bookId)
.then(function(cdata) {
bookData[cTypeData] = cdata;
}).done(function() {
resolve(bookData); // <- (B)
})
});
});
}
}
edit:
deferred is an anti-pattern discussed here. Honestly, it might be best to use a polyfill since Promise is in the es6 spec.

Promisify streams

I'm trying to promisify streams but it appears harder than I expected. Here is my attempt:
'use strict'
const Promise = require('bluebird')
const Twitter = require('twitter')
const TwitterStream = module.exports = function TwitterStream (config) {
// init Twitter Streaming API for OAuth
this.stream = new Twitter({
consumer_key: config.get('/twitter/consumerKey'),
consumer_secret: config.get('/twitter/consumerSecret'),
access_token_key: config.get('/twitter/accessTokenKey'),
access_token_secret: config.get('/twitter/accessTokenSecret')
})
.stream('statuses/filter', {
track: config.get('/twitter/track')
})
}
TwitterStream.prototype.receive = function () {
return new Promise((resolve, reject) => {
this.stream.on('data', resolve).on('error', reject)
})
}
TwitterStream.prototype.destroy = function () {
this.stream.destroy()
}
The main problem is that when I create the object
const stream = new TwitterStream(config)
stream.receive().then((data) => console.log(data))
when I execute only one object is read. no other data are streamed.
TwitterStream.prototype.receive = function () {
return new Promise((resolve, reject) => {
this.stream
.on('data', (data) => resolve(data)
.on('error', (error) => reject(error))
})
}
By using Rx extensions, it's pretty straightforward:
TwitterStream.prototype.receive = function () {
return Rx.Observable.create((observer) => {
this.stream
.on('data', (data) => observer.onNext(data))
.on('error', (err) => observer.onError(err));
});
}
And then
const stream = new TwitterStream(config)
stream.receive().subscribe((data) => console.log(data));
You need to return a promise in the callback of the stream.on function. Right now, the receive method when being called just returns a promise which once resolved returns the value or error.
Here is a not tested and most likely still buggy code to illustrate how you could do it with promises:
function defer() {
var resolve, reject;
var promise = new Promise(function() {
resolve = arguments[0];
reject = arguments[1];
});
return {
resolve: resolve,
reject: reject,
promise: promise
};
}
TwitterStream.prototype.receive = function() {
this.stream
.on('data', data => {
this.dataCache = this.dataCache || [];
this.dataCache.push(data);
this.tryToSendData()
})
.on('end', () => {
this.finished = true;
this.tryToSendData()
})
.on('error', err => {
this.lastError = err;
// error handling still missing
})
return this;
}
TwitterStream.prototype.tryToSendData = function() {
if (this.defered) {
let defered = this.defered;
this.defered = null;
// if data is available or finished then pass the first element of buffer (or undefined)
defered.resolve(this.dataCache.shift())
}
}
TwitterStream.prototype.getNextData = function() {
if (this.dataCache.length > 0 || this.finished) {
// if data is available or finished then pass the first element of buffer (or undefined)
return Promise.resolve(this.dataCache.shift());
} else {
// otherwise we need a defered object
this.defered = defer();
}
}
The usage could then look like this:
stream.receive().getNextData()
.then(function processData(data) {
if (data) {
console.dir(data);
// if data is available then continue requestin the data
return stream.getNextData().then(processData);
}
})
It is a rare case where you could use Deferreds.
I think you might want to take a look at my, already promisified streams in scramjet.
For your Twitter example this code should work well:
const stream = new Twitter({
consumer_key: config.get('/twitter/consumerKey'),
consumer_secret: config.get('/twitter/consumerSecret'),
access_token_key: config.get('/twitter/accessTokenKey'),
access_token_secret: config.get('/twitter/accessTokenSecret')
})
.stream('statuses/filter', {
track: config.get('/twitter/track')
})
.pipe(new scramjet.DataStream)
Then perform any transformations you like... for example map the stream somehow and accumulate the stream into an array when you're done.
stream.map(
function (a) { return modifyTheTweetSomehow(a); } // a Promise can be returned here
).accumulate(
function(a, i) { a.push(i); },
[]
) // this returns a Promise that will be resolved on stream end.
I hope you like it. :)

Look for Promise bluebird code review for node.js

When and where need to use new Promise(Function<Function resolve, Function reject> resolver) -> Promise
My Sample code:
userInfo.js
var Promise = require('bluebird');
var winston = require('winston');
var _ = require('lodash');
var request = Promise.promisify(require("request"));
exports.getWeather = function (data) {
var cityName = data.userProfile.city;
return request("http://0.0.0.0:3003/api/Weather/byCity?city=" + cityName).spread(function (res, body) {
var result = JSON.parse(body).data;
return _.merge(data, result);
});
};
exports.getUserProfile = function (userId) {
return new Promise(function (resolve, reject) {
request("http://0.0.0.0:3003/api/UserProfile/getUserProfile?id=" + userId).spread(function (res, body) {
var result = JSON.parse(body).data;
resolve(result);
});
})
};
exports.getEvents = function (data) {
var cityName = data.userProfile.city;
return request("http://0.0.0.0:3003/api/Events/byCity?city=" + cityName).spread(function (res, body) {
var result = JSON.parse(body).data;
return _.merge(data, result);
});
};
exports.getFashion = function (data) {
var gender = data.userProfile.gender;
return request("http://0.0.0.0:3003/api/Fashion/byGender?gender=" + gender).spread(function (res, body) {
var result = JSON.parse(body).data;
return _.merge(data, result);
});
};
exports.displayDetail = function (data) {
console.log(data);
};
Above code I try call in 2 way in promise
getUserProfile.js
var userInfo = require('./userInfo');
module.exports = function(){
return userInfo.getUserProfile(3)
.then(userInfo.getFashion)
.then(userInfo.getEvents)
.then(userInfo.getWeather)
.then(userInfo.displayDetail)
.catch(function (e) {
console.log('Error:');
console.error(e.stack)
})
.finally(function () {
console.log('done');
});
}
2nd way:
getUserInformation.js
var userInfo = require('./userInfo');
module.exports = function () {
return new Promise(function (resolve, reject) {
resolve(3);
})
.then(userInfo.getUserProfile)
.then(userInfo.getFashion)
.then(userInfo.getEvents)
.then(userInfo.getWeather)
.then(userInfo.displayDetail)
.catch(function (e) {
console.log('Error:');
console.error(e.stack)
})
.finally(function () {
console.log('done');
});
};
getDetails.js
var userInfo = require('./getUserInformation');
userInfo()
.then(function(){
console.log('getDetails done')
})
.catch(function (e) {
console.log('Error:');
console.error(e.stack)
})
.finally(function () {
console.log('done');
});
please let me know what the difference and is there any issues by using these way?
exports.getUserProfile = function (userId) {
return new Promise(function (resolve, reject) {
request("http://0.0.0.0:3003/api/UserProfile/getUserProfile?id=" + userId).spread(function (res, body) {
var result = JSON.parse(body).data;
resolve(result);
});
})
};
Please don't do this. Just return from the callback, and return the promise created by then, like you have done it in your other three methods.
return userInfo.getUserProfile(3)
.then(…)
vs.
return new Promise(function (resolve, reject) {
resolve(3);
})
.then(userInfo.getUserProfile)
.then(…)
Well, the first one is much more readable and concise. They're pretty much equivalent except for the case that getUserProfile does throw synchronously, which it shouldn't anyway. Also in the first case getUserProfile is invoked as a method on userInfo, while in the second case it's just a callback function, the this in the calls will be different.
The second pattern can be tremendously simplified though by using Promise.resolve instead of the new Promise constructor:
return Promise.resolve(3)
.then(userInfo.getUserProfile)
.then(…)
This is totally fine, and aligns better with the rest of the chain. Speaking of which, …
.then(userInfo.getFashion)
.then(userInfo.getEvents)
.then(userInfo.getWeather)
where each of the functions returns a promise that resolves with
additional data merged into its argument
is not exactly the best way to solve this. Yes, it ensures that these three functions are called after each other, and is an acceptable pattern for that case. However, in your case you're mixing the request calls to the API with that argument-extraction and result-merging in the same function; which by the separation of concerns you shouldn't. Rather make the functions pure
exports.… = function (arg) {
return request("http://0.0.0.0:3003/api/…?…=" + arg).spread(function (res, body) {
return JSON.parse(body).data;
});
};
And now you can combine them separately - and not only in sequence, but also in parallel:
userInfo.getUserProfile(3)
.then(function(data) {
var p = data.userProfile;
return Promise.prop({
userProfile: 0,
fashion: userInfo.getFashion(p.gender), // `\
events: userInfo.getEvents(p.city), // }=> execute requests in parallel
weather: userInfo.getWeather(p.city) // ./
});
})
.then(userInfo.displayDetail)
.catch(function (e) {
console.error('Error:', e.stack)
});
The first way is much more readable, and there's no benefit to starting the chain with a promise that returns a constant as in your second way.
They both do effectively the same thing, with one caveat: In your second example (Starting the chain with a Promise), the getUserProfile call will be run on the next tick (Similar to if you'd thrown it in a setTimeout 0) rather than atomically.

Categories

Resources