Node chain of promises and code readability - javascript

I'm trying to send several request/responses from a node server, and because they are async, I had to dive into and start learning promises. Im using bluebird and node as well as request to send requests.
Im looking to establish a certificate chain, and my server is acting trusted third party. I have the following
function send_certificates (addr) {
return send_csr_request(addr)
.then(function(csr) {
return new Promise(function(resolve,reject) {
resolve(sign_device_cert(csr))}
)}).then(function(signed_cert) {
return new Promise(function(resolve,reject) {
//another resolve here?
resolve(send_cert(signed_cert));
})
});
}
Ideally I would like to slap on another request to this "promise-chain", something like resolve(send_cert(caroot_cert))
and just generally be able to modify this chain of reqeust/responses a bit better. I feel like there is a lot of boilerplate code just to call these methods. Is there any other, more manageable and readable way to do this?
Also I'm not sure if I need to promisify all these methods? If, say, sign_device_cert returns a Promise, how do I chain it with .then() calls?
EDIT
Here is my function that sends off a request..
function send_ca_cert(cert) {
const url_string = "http://myaddress.com";
var request_options = {
url : url_string,
proxy : my_proxy,
body: pki.certificateToPem(cert),
headers: { "someHeader : "somevalue"}
};
return new Promise((resolve,reject) => {
request.postAsync(request_options, function(error, response, body) {
if (!error && response.statusCode == 200) {
console.log("Sent off ca cert" );
resolve();
}
reject();
});
});
}
When I call then(send_cert).then(send_ca_cert) i get the prints
Sent off ca cert
Sent off cert
How come they don't respect the Promises?

You don't have to wrap promises in promises. Do just
function send_certificates (addr) {
return send_csr_request(addr)
.then(sign_device_cert)
.then(send_cert);
}
This should work in your case (at least if your code works).
Sometimes, you can't just pass the original function because it needs different arguments (or a different context). In those cases you still can avoid creating a new promise:
return functionReturningAPromise()
.then(otherFunctionReturningAPromise) // takes as parameter the result of the previous one
.then(function(someValue){
return yetAnotherFunctionReturningAPromise(22, someValue);
});

You can write code in this fashion that will increase the code readability.
let promise = someAsyncFunction();
promise = promise.then((data) => { // here data is value if returned by function
return someAnotherAsyncCall();
});
promise = promise.then((data) => {// here data is value if returned by the previous function //}
return someAnotherAsyncCall();
});
// Similarly you can carry on!
// Don't forget to use arrow function they also help to increase the code readability and must use return statement at the end of promises to avoid promise chain break;

Related

What happens if i don't resolve a Promise? [duplicate]

I have a scenario where I am returning a promise.
The promise is basically triggered by an ajax request.
On rejecting the promise it shows an error dialog that there is a server error.
What I want to do is when the response code is 401, I neither want to resolve the promise nor reject it (because it already shows the error dialog). I want to simply redirect to the login page.
My code looks something like this:
function makeRequest(ur, params) {
return new Promise(function (resolve, reject) {
fetch(url, params).then((response) => {
let status = response.status;
if (status >= 200 && status < 300) {
response.json().then((data) => {
resolve(data);
});
} else {
if (status === 401) {
redirectToLoginPage();
} else {
response.json().then((error) => {
if (!error.message) {
error.message = constants.SERVER_ERROR;
}
reject({ status, error });
});
}
}
});
});
}
As you can see, if the status is 401, I am redirecting to the login page. The promise is neither resolved nor rejected.
Is this code OK, or is there any better way to accomplish this?
A promise is just an object with properties in Javascript. There's no magic to it. So failing to resolve or reject a promise just fails to ever change the state from "pending" to anything else. This doesn't cause any fundamental problem in Javascript because a promise is just a regular Javascript object. The promise will still get garbage collected (even if still pending) if no code keeps a reference to the promise.
The real consequence here is what does that mean to the consumer of the promise if its state is never changed? Any .then() or .catch() listeners for resolve or reject transitions will never get called. Most code that uses promises expects them to resolve or reject at some point in the future (that's why promises are used in the first place). If they don't, then that code generally never gets to finish its work.
Or if code is using await on the promise instead of .then(), then that function will just remain suspended forever on that await. The rest of the function will be dead code and will never execute.
It's possible that you could have some other code that finishes the work for that task and the promise is just abandoned without ever doing its thing. There's no internal problem in Javascript if you do it that way, but it is not how promises were designed to work and is generally not how the consumer of promises expect them to work.
As you can see if the status is 401, I am redirecting to login page.
Promise is neither resolved nor rejected.
Is this code OK? Or is there any better way to accomplish this.
In this particular case, it's all OK and a redirect is a somewhat special and unique case. A redirect to a new browser page will completely clear the current page state (including all Javascript state) so it's perfectly fine to take a shortcut with the redirect and just leave other things unresolved. The system will completely reinitialize your Javascript state when the new page starts to load so any promises that were still pending will get cleaned up.
I think the "what happens if we don't resolve reject" has been answered fine - it's your choice whether to add a .then or a .catch.
However, Is this code OK? Or is there any better way to accomplish this. I would say there are two things:
You are wrapping a Promise in new Promise when it is not necessary and the fetch call can fail, you should act on that so that your calling method doesn't sit and wait for a Promise which will never be resolved.
Here's an example (I think this should work for your business logic, not 100% sure):
const constants = {
SERVER_ERROR: "500 Server Error"
};
function makeRequest(url,params) {
// fetch already returns a Promise itself
return fetch(url,params)
.then((response) => {
let status = response.status;
// If status is forbidden, redirect to Login & return nothing,
// indicating the end of the Promise chain
if(status === 401) {
redirectToLoginPage();
return;
}
// If status is success, return a JSON Promise
if(status >= 200 && status < 300) {
return response.json();
}
// If status is a failure, get the JSON Promise,
// map the message & status, then Reject the promise
return response.json()
.then(json => {
if (!json.message) {
json.message = constants.SERVER_ERROR;
}
return Promise.reject({status, error: json.message});
})
});
}
// This can now be used as:
makeRequest("http://example", {})
.then(json => {
if(typeof json === "undefined") {
// Redirect request occurred
}
console.log("Success:", json);
})
.catch(error => {
console.log("Error:", error.status, error.message);
})
By contrast, calling your code using:
makeRequest("http://example", {})
.then(info => console.log("info", info))
.catch(err => console.log("error", err));
Will not log anything because the call to http://example will fail, but the catch handler will never execute.
As others stated it's true that it's not really an issue if you don't resolve/reject a promise. Anyway I would solve your problem a bit different:
function makeRequest(ur,params) {
return new Promise(function(resolve,reject) {
fetch(url,params)
.then((response) => {
let status = response.status;
if (status >= 200 && status < 300) {
response.json().then((data) => {
resolve(data);
})
}
else {
reject(response);
}
})
});
}
makeRequest().then(function success(data) {
//...
}, function error(response) {
if (response.status === 401) {
redirectToLoginPage();
}
else {
response.json().then((error) => {
if (!error.message) {
error.message = constants.SERVER_ERROR;
}
//do sth. with error
});
}
});
That means I would reject every bad response state and then handle this in your error handler of your makeRequest.
It works and isn't really a problem, except when a caller of makeRequest expects of promise to fulfil. So, you're breaking the contract there.
Instead, you could defer the promise, or (in this case) reject with status code/error.
The ECMAScript spec explains the purpose of promises and new Promise():
A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.
25.6.3.1 Promise ( executor )
NOTE The executor argument must be a function object. It is called for initiating and reporting completion of the possibly deferred action represented by this Promise object.
You should use promises to obtain future values. Furthermore, to keep your code concise and direct, you should only use promises to obtain future values, and not to do other things.
Since you’ve also mixed program control flow (redirection logic) into your promise’s “executor” logic, your promise is no longer “a placeholder for the results of a computation;” rather, it’s now a little JavaScript program masquerading as a promise.
So, instead of wrapping this JavaScript program inside a new Promise(), I recommend just writing it like a normal JavaScript program:
async function makeRequest(url, params) {
let response = await fetch(url, params);
let { status } = response;
if (status >= 200 && status < 300) {
let data = await response.json();
successLogic(data);
} else if (status === 401) {
redirectToLoginPage();
} else {
let error = await response.json()
if (!error.message) {
error.message = constants.SERVER_ERROR;
}
errorLogic({ status, error });
}
}

JS Promise - instantly retrieve some data from a function that returns a Promise

Can anyone recommend a pattern for instantly retrieving data from a function that returns a Promise?
My (simplified) example is an AJAX preloader:
loadPage("index.html").then(displayPage);
If this is downloading a large page, I want to be able to check what's happening and perhaps cancel the process with an XHR abort() at a later stage.
My loadPage function used to (before Promises) return an id that let me do this later:
var loadPageId = loadPage("index.html",displayPage);
...
doSomething(loadPageId);
cancelLoadPage(loadPageId);
In my new Promise based version, I'd imagine that cancelLoadPage() would reject() the original loadPage() Promise.
I've considered a few options all of which I don't like. Is there a generally accepted method to achieve this?
Okay, let's address your bounty note first.
[Hopefully I'll be able to grant the points to someone who says more than "Don't use promises"... ]
Sorry, but the answer here is: "Don't use promises". ES6 Promises have three possible states (to you as a user): Pending, Resolved and Rejected (names may be slightly off).
There is no way for you to see "inside" of a promise to see what has been done and what hasn't - at least not with native ES6 promises. There was some limited work (in other frameworks) done on promise notifications, but those did not make it into the ES6 specification, so it would be unwise of you to use this even if you found an implementation for it.
A promise is meant to represent an asynchronous operation at some point in the future; standalone, it isn't fit for this purpose. What you want is probably more akin to an event publisher - and even that is asynchronous, not synchronous.
There is no safe way for you to synchronously get some value out of an asynchronous call, especially not in JavaScript. One of the main reasons for this is that a good API will, if it can be asynchronous, will always be asynchronous.
Consider the following example:
const promiseValue = Promise.resolve(5)
promiseValue.then((value) => console.log(value))
console.log('test')
Now, let's assume that this promise (because we know the value ahead of time) is resolved synchronously. What do you expect to see? You'd expect to see:
> 5
> test
However, what actually happens is this:
> test
> 5
This is because even though Promise.resolve() is a synchronous call that resolves an already-resolved Promise, then() will always be asynchronous; this is one of the guarantees of the specification and it is a very good guarantee because it makes code a lot easier to reason about - just imagine what would happen if you tried to mix synchronous and asynchronous promises.
This applies to all asynchronous calls, by the way: any action in JavaScript that could potentially be asynchronous will be asynchronous. As a result, there is no way for you do any kind of synchronous introspection in any API that JavaScript provides.
That's not to say you couldn't make some kind of wrapper around a request object, like this:
function makeRequest(url) {
const requestObject = new XMLHttpRequest()
const result = {
}
result.done = new Promise((resolve, reject) => {
requestObject.onreadystatechange = function() {
..
}
})
requestObject.open(url)
requestObject.send()
return requestObject
}
But this gets very messy, very quickly, and you still need to use some kind of asynchronous callback for this to work. This all falls down when you try and use Fetch. Also note that Promise cancellation is not currently a part of the spec. See here for more info on that particular bit.
TL:DR: synchronous introspection is not possible on any asynchronous operation in JavaScript and a Promise is not the way to go if you were to even attempt it. There is no way for you to synchronously display information about a request that is on-going, for example. In other languages, attempting to do this would require either blocking or a race condition.
Well. If using angular you can make use of the timeout parameter used by the $http service if you need to cancel and ongoing HTTP request.
Example in typescript:
interface ReturnObject {
cancelPromise: ng.IPromise;
httpPromise: ng.IHttpPromise;
}
#Service("moduleName", "aService")
class AService() {
constructor(private $http: ng.IHttpService
private $q: ng.IQService) { ; }
doSomethingAsynch(): ReturnObject {
var cancelPromise = this.$q.defer();
var httpPromise = this.$http.get("/blah", { timeout: cancelPromise.promise });
return { cancelPromise: cancelPromise, httpPromise: httpPromise };
}
}
#Controller("moduleName", "aController")
class AController {
constructor(aService: AService) {
var o = aService.doSomethingAsynch();
var timeout = setTimeout(() => {
o.cancelPromise.resolve();
}, 30 * 1000);
o.httpPromise.then((response) => {
clearTimeout(timeout);
// do code
}, (errorResponse) => {
// do code
});
}
}
Since this approach already returns an object with two promises the stretch to include any synchronous operation return data in that object is not far.
If you can describe what type of data you would want to return synchronously from such a method it would help to identify a pattern. Why can it not be another method that is called prior to or during your asynchronous operation?
You can kinda do this, but AFAIK it will require hacky workarounds. Note that exporting the resolve and reject methods is generally considered a promise anti-pattern (i.e. sign you shouldn't be using promises). See the bottom for something using setTimeout that may give you what you want without workarounds.
let xhrRequest = (path, data, method, success, fail) => {
const xhr = new XMLHttpRequest();
// could alternately be structured as polymorphic fns, YMMV
switch (method) {
case 'GET':
xhr.open('GET', path);
xhr.onload = () => {
if (xhr.status < 400 && xhr.status >= 200) {
success(xhr.responseText);
return null;
} else {
fail(new Error(`Server responded with a status of ${xhr.status}`));
return null;
}
};
xhr.onerror = () => {
fail(networkError);
return null;
}
xhr.send();
return null;
}
return xhr;
case 'POST':
// etc.
return xhr;
// and so on...
};
// can work with any function that can take success and fail callbacks
class CancellablePromise {
constructor (fn, ...params) {
this.promise = new Promise((res, rej) => {
this.resolve = res;
this.reject = rej;
fn(...params, this.resolve, this.reject);
return null;
});
}
};
let p = new CancellablePromise(xhrRequest, 'index.html', null, 'GET');
p.promise.then(loadPage).catch(handleError);
// times out after 2 seconds
setTimeout(() => { p.reject(new Error('timeout')) }, 2000);
// for an alternative version that simply tells the user when things
// are taking longer than expected, NOTE this can be done with vanilla
// promises:
let timeoutHandle = setTimeout(() => {
// don't use alert for real, but you get the idea
alert('Sorry its taking so long to load the page.');
}, 2000);
p.promise.then(() => clearTimeout(timeoutHandle));
Promises are beautiful. I don't think there is any reason that you can not handle this with promises. There are three ways that i can think of.
The simplest way to handle this is within the executer. If you would like to cancel the promise (like for instance because of timeout) you just define a timeout flag in the executer and turn it on with a setTimeout(_ => timeout = true, 5000) instruction and resolve or reject only if timeout is false. ie (!timeout && resolve(res) or !timeout && reject(err)) This way your promise indefinitely remains unresolved in case of a timeout and your onfulfillment and onreject functions at the then stage never gets called.
The second is very similar to the first but instead of keeping a flag you just invoke reject at the timeout with proper error description. And handle the rest at the then or catch stage.
However if you would like to carry the id of your asych operation to the sync world then you can also do it as follows;
In this case you have to promisify the async function yourself. Lets take an example. We have an async function to return the double of a number. This is the function
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
We would like to use promises. So normally we need a promisifier function which will take our async function and return another function which when run, takes our data and returns a promise. Right..? So here is the promisifier function;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
Lets se how they work together;
function promisify(fun){
return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}
function doubleAsync(data,cb){
setTimeout(_ => cb(false, data*2),1000);
}
var doubleWithPromise = promisify(doubleAsync);
doubleWithPromise(100).then(v => console.log("The asynchronously obtained result is: " + v));
So now you see our doubleWithPromise(data) function returns a promise and we chain a then stage to it and access the returned value.
But what you need is not only a promise but also the id of your asynch function. This is very simple. Your promisified function should return an object with two properties; a promise and an id. Lets see...
This time our async function will return a result randomly in 0-5 secs. We will obtain it's result.id synchronously along with the result.promise and use this id to cancel the promise if it fails to resolve within 2.5 secs. Any figure on console log Resolves in 2501 msecs or above will result nothing to happen and the promise is practically canceled.
function promisify(fun){
return function(data){
var result = {id:null, promise:null}; // template return object
result.promise = new Promise((resolve,reject) => result.id = fun(data, (err,res) => err ? reject(err) : resolve(res)));
return result;
};
}
function doubleAsync(data,cb){
var dur = ~~(Math.random()*5000); // return the double of the data within 0-5 seconds.
console.log("Resolve in " + dur + " msecs");
return setTimeout(_ => cb(false, data*2),dur);
}
var doubleWithPromise = promisify(doubleAsync),
promiseDataSet = doubleWithPromise(100);
setTimeout(_ => clearTimeout(promiseDataSet.id),2500); // give 2.5 seconds to the promise to resolve or cancel it.
promiseDataSet.promise
.then(v => console.log("The asynchronously obtained result is: " + v));
You can use fetch(), Response.body.getReader(), where when .read() is called returns a ReadableStream having a cancel method, which returns a Promise upon cancelling read of the stream.
// 58977 bytes of text, 59175 total bytes
var url = "https://gist.githubusercontent.com/anonymous/"
+ "2250b78a2ddc80a4de817bbf414b1704/raw/"
+ "4dc10dacc26045f5c48f6d74440213584202f2d2/lorem.txt";
var n = 10000;
var clicked = false;
var button = document.querySelector("button");
button.addEventListener("click", () => {clicked = true});
fetch(url)
.then(response => response.body.getReader())
.then(reader => {
var len = 0;
reader.read().then(function processData(result) {
if (result.done) {
// do stuff when `reader` is `closed`
return reader.closed.then(function() {
return "stream complete"
});
};
if (!clicked) {
len += result.value.byteLength;
}
// cancel stream if `button` clicked or
// to bytes processed is greater than 10000
if (clicked || len > n) {
return reader.cancel().then(function() {
return "read aborted at " + len + " bytes"
})
}
console.log("len:", len, "result value:", result.value);
return reader.read().then(processData)
})
.then(function(msg) {
alert(msg)
})
.catch(function(err) {
console.log("err", err)
})
});
<button>click to abort stream</button>
The method I am currently using is as follows:
var optionalReturnsObject = {};
functionThatReturnsPromise(dataToSend, optionalReturnsObject ).then(doStuffOnAsyncComplete);
console.log("Some instant data has been returned here:", optionalReturnsObject );
For me, the advantage of this is that another member of my team can use this in a simple way:
functionThatReturnsPromise(data).then(...);
And not need to worry about the returns object. An advanced user can see from the definitions what is going on.

What happens if you don't resolve or reject a promise?

I have a scenario where I am returning a promise.
The promise is basically triggered by an ajax request.
On rejecting the promise it shows an error dialog that there is a server error.
What I want to do is when the response code is 401, I neither want to resolve the promise nor reject it (because it already shows the error dialog). I want to simply redirect to the login page.
My code looks something like this:
function makeRequest(ur, params) {
return new Promise(function (resolve, reject) {
fetch(url, params).then((response) => {
let status = response.status;
if (status >= 200 && status < 300) {
response.json().then((data) => {
resolve(data);
});
} else {
if (status === 401) {
redirectToLoginPage();
} else {
response.json().then((error) => {
if (!error.message) {
error.message = constants.SERVER_ERROR;
}
reject({ status, error });
});
}
}
});
});
}
As you can see, if the status is 401, I am redirecting to the login page. The promise is neither resolved nor rejected.
Is this code OK, or is there any better way to accomplish this?
A promise is just an object with properties in Javascript. There's no magic to it. So failing to resolve or reject a promise just fails to ever change the state from "pending" to anything else. This doesn't cause any fundamental problem in Javascript because a promise is just a regular Javascript object. The promise will still get garbage collected (even if still pending) if no code keeps a reference to the promise.
The real consequence here is what does that mean to the consumer of the promise if its state is never changed? Any .then() or .catch() listeners for resolve or reject transitions will never get called. Most code that uses promises expects them to resolve or reject at some point in the future (that's why promises are used in the first place). If they don't, then that code generally never gets to finish its work.
Or if code is using await on the promise instead of .then(), then that function will just remain suspended forever on that await. The rest of the function will be dead code and will never execute.
It's possible that you could have some other code that finishes the work for that task and the promise is just abandoned without ever doing its thing. There's no internal problem in Javascript if you do it that way, but it is not how promises were designed to work and is generally not how the consumer of promises expect them to work.
As you can see if the status is 401, I am redirecting to login page.
Promise is neither resolved nor rejected.
Is this code OK? Or is there any better way to accomplish this.
In this particular case, it's all OK and a redirect is a somewhat special and unique case. A redirect to a new browser page will completely clear the current page state (including all Javascript state) so it's perfectly fine to take a shortcut with the redirect and just leave other things unresolved. The system will completely reinitialize your Javascript state when the new page starts to load so any promises that were still pending will get cleaned up.
I think the "what happens if we don't resolve reject" has been answered fine - it's your choice whether to add a .then or a .catch.
However, Is this code OK? Or is there any better way to accomplish this. I would say there are two things:
You are wrapping a Promise in new Promise when it is not necessary and the fetch call can fail, you should act on that so that your calling method doesn't sit and wait for a Promise which will never be resolved.
Here's an example (I think this should work for your business logic, not 100% sure):
const constants = {
SERVER_ERROR: "500 Server Error"
};
function makeRequest(url,params) {
// fetch already returns a Promise itself
return fetch(url,params)
.then((response) => {
let status = response.status;
// If status is forbidden, redirect to Login & return nothing,
// indicating the end of the Promise chain
if(status === 401) {
redirectToLoginPage();
return;
}
// If status is success, return a JSON Promise
if(status >= 200 && status < 300) {
return response.json();
}
// If status is a failure, get the JSON Promise,
// map the message & status, then Reject the promise
return response.json()
.then(json => {
if (!json.message) {
json.message = constants.SERVER_ERROR;
}
return Promise.reject({status, error: json.message});
})
});
}
// This can now be used as:
makeRequest("http://example", {})
.then(json => {
if(typeof json === "undefined") {
// Redirect request occurred
}
console.log("Success:", json);
})
.catch(error => {
console.log("Error:", error.status, error.message);
})
By contrast, calling your code using:
makeRequest("http://example", {})
.then(info => console.log("info", info))
.catch(err => console.log("error", err));
Will not log anything because the call to http://example will fail, but the catch handler will never execute.
As others stated it's true that it's not really an issue if you don't resolve/reject a promise. Anyway I would solve your problem a bit different:
function makeRequest(ur,params) {
return new Promise(function(resolve,reject) {
fetch(url,params)
.then((response) => {
let status = response.status;
if (status >= 200 && status < 300) {
response.json().then((data) => {
resolve(data);
})
}
else {
reject(response);
}
})
});
}
makeRequest().then(function success(data) {
//...
}, function error(response) {
if (response.status === 401) {
redirectToLoginPage();
}
else {
response.json().then((error) => {
if (!error.message) {
error.message = constants.SERVER_ERROR;
}
//do sth. with error
});
}
});
That means I would reject every bad response state and then handle this in your error handler of your makeRequest.
It works and isn't really a problem, except when a caller of makeRequest expects of promise to fulfil. So, you're breaking the contract there.
Instead, you could defer the promise, or (in this case) reject with status code/error.
The ECMAScript spec explains the purpose of promises and new Promise():
A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.
25.6.3.1 Promise ( executor )
NOTE The executor argument must be a function object. It is called for initiating and reporting completion of the possibly deferred action represented by this Promise object.
You should use promises to obtain future values. Furthermore, to keep your code concise and direct, you should only use promises to obtain future values, and not to do other things.
Since you’ve also mixed program control flow (redirection logic) into your promise’s “executor” logic, your promise is no longer “a placeholder for the results of a computation;” rather, it’s now a little JavaScript program masquerading as a promise.
So, instead of wrapping this JavaScript program inside a new Promise(), I recommend just writing it like a normal JavaScript program:
async function makeRequest(url, params) {
let response = await fetch(url, params);
let { status } = response;
if (status >= 200 && status < 300) {
let data = await response.json();
successLogic(data);
} else if (status === 401) {
redirectToLoginPage();
} else {
let error = await response.json()
if (!error.message) {
error.message = constants.SERVER_ERROR;
}
errorLogic({ status, error });
}
}

testing using Babel,Bluebird Promises and Mocha runs tests in wrong order

I am a bit confused and I am also a newcomer to promises.
My problem is that the beforeEach function runs in random order (how is this possible?) so that the delete query is not run first. Also the order changes if I put console.log messages in the promise (??). What am I doing wrong here.
Note that the database (neo4j in this case) returns 200 OK in case there is a constrain violation that is why I am reading the result and then rejecting.
I have this test (using chai-as-promised):
beforeEach(function() {
return neodb.query('MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r',{}) //clear the db first
.then(neodb.query(query,{props:[{username:'arisAlexis'}]}))
.then(neodb.query(query,{props:[{username:'marySilva'}]}))
.then(neodb.query(query,{props:[{username:'silviaBurakof'}]}));
});
it('create a username conflict',function() {
return User.create([{username:'arisAlexis'}]).should.be.rejected;
})
User.create is a static method that just runs this :
return db.query(query,{props:props});
so it returns a promise that is created here:
exports.query=function(query,params) {
return new Promise(function(resolve,reject) {
request.postAsync({
uri: dbUrl,
json: {statements: [{statement: query, parameters: params}]}
}).then(function(result) {
if (result[1].errors.length > 0) {
reject(new NeoError(result[1].errors[0].message,result[1].errors[0].code));
}
resolve(result[1].results);
});
});}
I am running mocha with babel-node.
I am also using promisify on request module and I am rejecting with a custom error (am I using it correctly ? In the documentation they throw the error).
You are executing all query functions at the same time, instead of waiting for the previous one to end:
.then(neodb.query(...)) // this executes `neodb.query()` right away
// and uses its return value as the callback
// function for the `.then()`.
Here's how to fix that:
beforeEach(function() {
return neodb.query('MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r',{}).then(function() {
return neodb.query(query,{props:[{username:'arisAlexis'}]});
}).then(function() {
return neodb.query(query,{props:[{username:'marySilva'}]});
}).then(function() {
return neodb.query(query,{props:[{username:'silviaBurakof'}]});
});
});

How to call Q promise notify within the promise chain

I need helps on notify() within the promise chain.
I have 3 promise base functions connect(), send(cmd), disconnect(). Now I would like to write another function to wrap those call in following manner with progress notification.
function bombard() {
return connect()
.then(function () {
var cmds = [/*many commands in string*/];
var promises = _.map(cmds, function (cmd) {
var deferred = Q.defer();
deferred.notify(cmd);
send(cmd).then(function (result) {
deferred.resovle(result);
});
return deferred.promise;
});
return Q.all(promises);
})
.finally(function () { return disconnect() })
}
Run the function like that
bombard.then(onResolve, onReject, function (obj) {
console.log(ob);
});
I supposed I will get notification for every command I have sent. However, it does not work as I expected. I get nothing actually.
Although I believe this is due to those notifications havn't propagated to outside promise, I have no idea on how to propagated those notifications on Q or wrapping that promise chain: connect, send, disconnect in a one deferred object.
Thanks
I have some good news and some bad news.
Very good! You have found out the problem with the notifications API and why it is being removed in Q in the v2 branch, being deprecated in newer libraries like Bluebird, and never included in ECMAScript 6. It really boils down to the fact promises are not event emitters.
The notifications API does not compose or aggregate very well. In fact, notifications being on promises does not make too much sense imo to begin with,.
Instead, I suggest using a progress notification even, kind of like IProgress in C#. I'm going to simulate all the actions with Q.delay() for isolation, your code will obviously make real calls
function connect(iProgress){
return Q.delay(1000).then(function(res){
iProgress(0.5,"Connecting to Database");
}).delay(1000).then(function(res){
iProgress(0.5,"Done Connecting");
});
}
function send(data,iProgress){
return Q.delay(200*Math.random() + 200).then(function(res){
iProgress(0.33, "Sent First Element");
}).delay(200*Math.random() + 400).then(function(){
iProgress(0.33, "Sent second Element");
}).delay(200*Math.random() + 500).then(function(){
iProgress(0.33, "Done sending!");
});
}
// disconnect is similar
Now, we can easily decide how our promises compose, for example:
function aggregateProgress(num){
var total = 0;
return function(progression,message){
total += progression;
console.log("Progressed ", ((total/num)*100).toFixed(2)+"%" );
console.log("Got message",message);
}
}
Which would let you do:
// bombard can accept iProgress itself if it needs to propagate it
function bombard() {
var notify = aggregateProgress(cmds.length+1);
return connect(notify)
.then(function () {
var cmds = [/*many commands in string*/];
return Q.all(cmds.map(function(command){ return send(command,notify); }));
});
}
Here is a complete and working fiddle to play with

Categories

Resources