How to not run a Promise before I ask to - node.js

I thought I had it all figured out regarding Promise, but this one actually throws me out of bed. When creating a new Promise with a executor taking two arguments, why is this method running before I either take then() or catch() at that promise
Running node 6.2.2.
import assert = require('assert');
describe("When working with promises", () => {
let ar = [1, 2, 3, 4, 5, 6];
beforeEach(() => {
})
it("should be perfectly fine but isn't when mapping to promises", (done) => {
ar.map(num => {
return new Promise((resolve, reject) => {
done(new Error('Why on earth is ' + num + ' called'));
})
})
done();
})
it("should be perfectly fine when mapping to methods", (done) => {
ar.map(num => {
return (resolve, reject) => {
done(new Error(num + ' is not called ever'));
}
})
done();
})
});
First test fails, and second test is successful.

If you check the documentation for Promise, you will find that the function that is given to the constructor is run immediately. It is supposed to kick off the asynchronous calculation and install the two callbacks there (so that resolve and reject are not invoked just now, but whenever that calculation completes or fails).
The Promise function is not the asynchronous calculation, it just wraps that calculation and the two callbacks you need to keep track of it together into a nice package (and returns immediately after this setup is done).
Your second example creates an anonymous function for each number, but does not invoke any of them.
why is this method running before I either take then() or catch() at that promise
The Promise does not care if you install any handlers/chains. The calculation will have been started (or not) no matter if anyone is watching it or not.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise:
[...] The executor function is executed immediately by the Promise implementation, passing resolve and reject functions [...].
That's the way, the Promise implementation works. You can also create a new promise and attach chained then() or catch() handlers later when the promise already succeeded or failed.
const p = new Promise(someExecutor);
// do some other things
p.then(someHandler);
// do some other things
p.catch(someOtherHandler);
If you do not want the executor to execute, yet, then don't pass it to the Promise constructur, yet.

To achieve the same behavior you have to wrap promises in a factory. You can use a lambda functions for that.
it("should be perfectly fine but isn't when mapping to promises", (done) => {
ar.map(num => {
return () => new Promise((resolve, reject) => {
done(new Error('Why on earth is ' + num + ' called'));
})
})
done();
})
So you have stateless factories to call later without creating promises (that run immediately) during map.
It permits you to compose and join Promises for later batch execution using Promise.all, Promise.race, or custom chaining.

If you want something to run only (i.e. not before) a "then()" handler is attached, then you can use plain then-ables ( https://promisesaplus.com/ )
var runLater = {
then: function(resolve, reject) {
var p = new Promise (...); // Creating a promise is optional
resolve(p); // you can return a plain value
}
}
Note this is not a Promise, so it does not have a "catch" method.
But it can be given to any function expecting a promise:
`Promise.all([runLater])
return as result from a then handled .then(function() { return runLater; })
Promise.resolve(runLater) => Now it is a promise with then/catch
Any of those will call the runLater.then method.

Related

How can I test that a promise have been waited for (and not just created) using Sinon?

Let's say I have a function:
const someAction = async(): Promise<string> => {
/* do stuff */
};
And I have some code that just needs to run this action, ignoring the result. But I have a bug - I don't want for action to complete:
someAction();
Which, instead, should've been looked like this:
await someAction();
Now, I can check that this action was ran:
const actionStub = sinon.stub(someAction);
expect(actionStub).to.have.been.calledWith();
But what's the most concise way to check that this promise have been waited on?
I understand how to implement this myself, but I suspect it must have already been implemented in sinon or sinon-chai, I just can't find anything.
I can certainly say that nothing like this exists in sinon or sinon-chai.
This is a difficulty inherent to testing any promise-based function where the result isn't used. If the result is used, you know the promise has to be resolved before proceeding with said result. If it is not, things get more complex and kind of outside of the scope of what sinon can do for you with a simple stub.
A naive approach is to stub the action with a fake that sets some variable (local to your test) to track the status. Like so:
let actionComplete = false;
const actionStub = sinon.stub(someAction).callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
actionComplete = true;
resolve();
});
});
});
expect(actionStub).to.have.been.calledWith();
expect(actionComplete).to.be.true;
Of course, the problem here is that awaiting any promise, not necessarily this particular one, will pass this test, since the variable will get set on the next step of the event loop, regardless of what caused you to wait for that next step.
For example, you could make this pass with something like this in your code under test:
someAction();
await new Promise((resolve) => {
setImmediate(() => resolve());
});
A more robust approach will be to create two separate tests. One where the promise resolves, and one where the promise rejects. You can test to make sure the rejection causes the containing function to reject with the same error, which would not be possible if that specific promise was not awaited.
const actionStub = sinon.stub(someAction).resolves();
// In one test
expect(actionStub).to.have.been.calledWith();
// In another test
const actionError = new Error('omg bad error');
actionStub.rejects(actionError);
// Assuming your test framework supports returning promises from tests.
return functionUnderTest()
.then(() => {
throw new Error('Promise should have rejected');
}, (err) => {
expect(err).to.equal(actionError);
});
Some assertion libraries and extensions (maybe chai-as-promised) may have a way of cleaning up that use of de-sugared promises there. I didn't want to assume too much about the tools you're using and just tried to make sure the idea gets across.

How to fix - Promise { <pending> } in Node.js and use it as variable anywhere

I'm accessing google API for converting coordinates into detailed objects using node-geocoder library from npmjs. Everything went well and I'm getting the expected object from geocoder API. The problem started the moment when I thought of using the data outside the promise function. I want to use the values outside the promise/async-await function.
Below is the code I've tried, Pls take a look and help me. TIA...
function goecoderPromiseFunction() {
return new Promise(function (resolve, reject) {
geocoder.reverse({ lat: 45.767, lon: 4.833 })
.then(data => {
cityName = data[0].city;
resolve(cityName);
})
.catch(err => {
console.log(err);
});
});
}
async function app() {
var a = await goecoderPromiseFunction();
return a;
}
var a = app();
console.log("a->", a);
I expect the variable "a" should print the city name "Lyon", but it prints
a-> Promise { < pending > }
The promise returned by the app function is never consumed, that is why it remains in a pending state.
Call then on the app function to get the result :
app().then(a => console.log("a->", a));
You can also use async/await :
(async function() {
var a = await app();
console.log("a->", a);
})();
An asynchronous function actually returns a promise that 'resolves' to the function's return value. You are therefore assigning a promise to the value of a. If you are in the global scope, you obviously cannot use async/await so you need to use either a self-executing async function or you need to run
a.then(data => console.log('a->', data));
to get what you are looking for.
Find out more about async functions here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
It prints because console.log("a->", a); runs while promise haven't returned answer for a variable "a"
Note: you haven't used reject function, if there is an error you wont notice and may be that error was the required answer to be carried by variable "a" that's why it still pending i.e still waiting.
For more idea try to use reject function inside the catch block example reject(err)
instead of console it out as you've done

Promise map (node.js) with large list seems to stop iterating after certain index

I'm using the bluebird Promise library. From this library I'm using Promise.mapSeries() to perform a mapping operation on a very large list (size=747357).
The code looks as follows (psuedo):
function myFunc(data) {
return Promise.mapSeries(data, handler)
.then((data) => {
console.log('Success!');
return Promise.resolve(data);
})
.catch(console.log);
}
In the handler, I'm doing a couple of things:
Running other Promise functions (recursive)
Returning a new data structure to be stored in the resulting array
In the handler function, I add a console.log('Iter: ', i);.
So then this returns the iter # for each item that's been mapped. It slows and eventually stops at #288. Does this reflect some sort of limit I'm hitting?
I don't understand what the problem is - logic says perhaps this list is too large to handle with Promise.mapSeries.
Any advice or solutions would be appreciated.
Thanks in advance.
UPDATE:
Here's a snippet of the handler function:
function handler(v, i, len) {
return new Promise((resolve, reject) => {
promise
.then((data) => {
return recursivePromiseFn(data)
})
.then((data) => {
let _data = transformData(data);
statusLogger(i);
resolve(_data);
})
})
}
The problem was not with Promise.map or Promise.mapSeries. The problem was an infinite loop in one of my promise functions. This loop seemed to prevent the promise loop from continuing.
A noteworthy comment was that you should avoid using the Promise anti-pattern. This is true but was not the cause of the problem.

how to call function where parameter based on a promise

had following code which worked:
let sdk = new SDK({ name: "somevalue"});
module.exports= ()=> {
sdk.dothing();
}
I then needed to change the parameter to use data from an async function:
let asyncfunc = require('asyncfunc');
let sdk = new SDK({ name: (()=>{
return asyncfunc()
.then((data) => {
return data.value
})
});
module.exports= ()=> {
sdk.dothing();
}
Following the change, the call to new SDK is failing because the parameter passed is {} as the asyncFunc promise has not yet resolved.
I'm getting back into node after a year and new to promises. what is the proper way to do this?
As you've found, you can't pass in a promise to something that's expecting a string. You need to wait for the asynchronous operation to complete.
This means that your SDK won't be ready right away, so you have two options:
Change your module so it returns a promise for the needed value. Anyone who needs to use your module would need to use the returned promise.
Example:
let pSdk = asyncFunc()
.then(data => new SDK({ name: data.value }));
module.exports = () => pSdk.then(sdk => sdk.dothing());
Store an sdk value that's not populated immediately. Users of your module can obtain the SDK instance directly, but it might not be ready when they need it.
Example:
let sdk;
asyncFunc()
.then(data => sdk = new SDK({ name: data.value }));
module.exports = () => {
if(!sdk) { throw new Error("The SDK is not ready yet!"); }
return sdk.dothing();
};
if any bit of code, in node, is asynchronous then immediately next bit of code will be executed. it doesn't matter if the asynchronous code is wrapped in promise or not.( For codes wrapped in the promise the compiler will return a pending promise to be resoled or rejected and proceed to the next bit of code.) When you are creating an object using new SDK({ }) the name is having reference to a pending promise which is yet to be settled that's why your code is failing to fulfill your requirement. You can do it this way to resolve your problem.
asyncfunc()
.then((data) => {
return new SDK({ name: data.value });
}).then(function(sdk){
//do your work here using sdk
})
One important point to be noted here is you can't return from .then() to assign the value to any variable as you are doing. The value returned from .then() will be accessible from the next chained .then() not by outside global variable.Since you are exporting sdk.dothing() so you need to export it inside the last .then()

Intern: Testing async unit tests with ES6 promises

I have some async functions that I need to test via Intern. Each uses ES6 promises.
In the Intern docs, it says that test functions that return a promise pass as soon as that promise is resolved. However, I not only care about whether the async function I am testing is resolved/rejected, I also want to check to make sure the resolved value is correct.
Example: I have my function:
function getSomething() {
return new Promise((resolve, reject) => {
// do some async stuff and resolve when done
setTimeout(function() {
resolve('the value');
}, 1000);
});
}
Then I have the test method:
tdd.test('getSomething', function() {
return new Promise((resolve, reject) => {
getSomething().then(function(value) {
// I want to resolve/reject the promise BASED ON the assertion -- if the assertion fails, I want to reject the promise; if it succeeds, I want to resolve it
assert.equal(value, 'the value', 'getSomething should return "the value".');
resolve();
});
});
}
I've noticed this doesn't work -- if the assertion fails in my test, the resolve() never gets called.
How would I be able to resolve/reject the promise conditioned upon the assertion? Should I wrap the assertion in a try/catch? I didn't see any documentation on the Intern docs about this (and most were using this.async() instead of ES6 promises).
What's happening in your code is that the assertion in the getSomething callback is throwing an exception, and that's preventing resolve from being called. The getSomething promise will be rejected, but since you're not returning it in the wrapper promise's initializer, the wrapper promise is never resolved or rejected.
There is no need to wrap the promise returned by getSomething in a new promise. Just return it directly.

Resources