Promise: Then before Resolve - javascript

Very first thing I've ever done in Node.js, I'm writing an AWS Lambda function, and I want to check whether a custom attribute on a User has a value before doing anything else. Since I'm told Promises are the way to handle asynchronous methods synchronously, I wrote the function:
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var cogId = new AWS.CognitoIdentityServiceProvider();
exports.handler = function (event, context) {
if (event != null)
{
var identityId = context.identity.cognitoIdentityId;
if (event.userId != null)
{
var userId = event.userId;
PromiseConfirmIdNotSet(userId)
.then(SetId(userId, identityId))
.catch();
}
}
context.done(null, 'Hello World'); // SUCCESS with message
};
function PromiseConfirmIdNotSet(userId)
{
console.log('Entering function');
return new Promise(function (resolve, reject) {
console.log('Entering Promise');
cogId.adminGetUser({
UserPoolId: myUserPool,
UserId: userId
},
function (err, data) {
console.log('err = ' + JSON.stringify(err));
console.log('data = ' + JSON.stringify(err));
if (data != null && data.UserAttributes.Name == null) {
console.log('Calling resolve');
resolve();
} else {
console.log('Calling reject');
reject();
}
});
});
console.log('Exiting Promise');
}
function SetId(userId, identityId)
{
cogId.updateUserAttributes();
}
But when I run it, the console log shows "Entering function", then "Entering Promise", then the execution goes to SetId without ever having called the callback specified in adminGetUser.
If I let the debugger continue after the main flow is done, eventually I do get the logs from the callback function, so it does eventually run.
Why is the Promise skipping to the then without the resolve ever getting called?

.then accepts a function as an argument. When you do
PromiseConfirmIdNotSet(userId)
.then(SetId(userId, identityId))
.catch();
PromiseConfirmIdNotSet is called, and synchronously, SetId is called, while the interpreter tries to construct a Promise chain from the function passed to .then. (But SetId doesn't return a function) Then, after that, PromiseConfirmIdNotSet's asynchronous code runs, and the Promise resolves - which isn't in the order you want.
Change it so that SetId is only called after the promise returned by PromiseConfirmIdNotSet resolves:
PromiseConfirmIdNotSet(userId)
.then(() => SetId(userId, identityId))
.catch();
The problem is similar to why
addEventListener('click', fn());
doesn't work - you'd change it to , fn); or , () => fn());.
If you additionally want context.done to occur only after a successful SetId, then put the context.done call inside the .then:
PromiseConfirmIdNotSet(userId)
.then(() => {
SetId(userId, identityId);
context.done(null, 'Hello World'); // SUCCESS with message
});

You can simply use async-await for neat asynchronous functions. Here is your code with async await. Please check and let me know if you find any more issues.
exports.handler = async function (event, context) {
if (event != null)
{
var identityId = context.identity.cognitoIdentityId;
if (event.userId != null)
{
var userId = event.userId;
await PromiseConfirmIdNotSet(userId);
await SetId(userId, identityId);
}
}
await context.done(null, 'Hello World'); // SUCCESS with message
};
function PromiseConfirmIdNotSet(userId)
{
console.log('Entering function');
return new Promise(function (resolve, reject) {
console.log('Entering Promise');
cogId.adminGetUser({
UserPoolId: myUserPool,
UserId: userId
},
function (err, data) {
console.log('err = ' + JSON.stringify(err));
console.log('data = ' + JSON.stringify(err));
if (data != null && data.UserAttributes.Name == null) {
console.log('Calling resolve');
resolve();
} else {
console.log('Calling reject');
reject();
}
});
});
console.log('Exiting Promise');
}
function SetId(userId, identityId)
{
cogId.updateUserAttributes();
}

Related

Synchronizing in Node JS [duplicate]

When using a simple callback such as in the example below:
test() {
api.on( 'someEvent', function( response ) {
return response;
});
}
How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:
async test() {
return await api.on( 'someEvent' );
}
async/await is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on() to return a Promise for that to work. Something like this:
function apiOn(event) {
return new Promise(resolve => {
api.on(event, response => resolve(response));
});
}
Then
async function test() {
return await apiOn( 'someEvent' ); // await is actually optional here
// you'd return a Promise either way.
}
But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}
It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...) is fugly, but I have found an ok work-around using util.promisify (actually it also kinda does the same wrapping, just looks nicer).
function voidFunction(someArgs, callback) {
api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
callback(null, response_we_need);
});
}
The above function does not return anything, yet. We can make it return a Promise of the response passed in callback by doing:
const util = require('util');
const asyncFunction = util.promisify(voidFunction);
Now we can actually await the callback.
async function test() {
return await asyncFunction(args);
}
Some rules when using util.promisify
The callback must be the last argument of the function that is gonna be promisify
The supposed-callback must be in the form (err, res) => {...}
Funny thing is we do not need to ever specifically write what's the callback actually is.
async/await is magic. You can create a function asPromise to handle this kind of situations:
function asPromise(context, callbackFunction, ...args) {
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
if (context) {
callbackFunction.call(context, ...args);
} else {
callbackFunction(...args);
}
});
}
and then use it when you want:
async test() {
return await this.asPromise(this, api.on, 'someEvent');
}
the number of args is variable.
You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors
clickMe = async (value) => {
// begin to wait till the message gets here;
let {message, error} = await getMessage(value);
// if error is not null
if(error)
return console.log('error occured ' + error);
return console.log('message ' + message);
}
getMessage = (value) => {
//returning a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// if passed value is 1 then it is a success
if(value == 1){
resolve({message: "**success**", error: null});
}else if (value == 2){
resolve({message: null, error: "**error**"});
}
}, 1000);
});
}
clickWithTryCatch = async (value) => {
try{
//since promise reject in getMessage2
let message = await getMessage2(value);
console.log('message is ' + message);
}catch(e){
//catching rejects from the promise
console.log('error captured ' + e);
}
}
getMessage2 = (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value == 1)
resolve('**success**');
else if(value == 2)
reject('**error**');
}, 1000);
});
}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>
const getprice = async () => {
return await new Promise((resolve, reject) => {
binance.prices('NEOUSDT', (error, ticker) => {
if (error) {
reject(error)
} else {
resolve(ticker);
}
});
})}
router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});

Getting asynchronous function in external library with callback to work synchronously

I am using a library call to connect to my vendor. The libary call requires a callback in the call. Without a callback in the function, I can easily make this synchronous. With the Callback, everything I do is stuck in the callback and never bubbles it way out.
I have literally tried 100 different ways to get this to work.
function removeFromDNC(emailAddress, accessToken_in)
{
return new Promise( function(resolve, reject)
{
try{
const options =
{
auth: {
accessToken: accessToken_in
}
, soapEndpoint: 'https://webservice.XXX.exacttarget.com/Service.asmx'
};
var co = {
"CustomerKey": "DNC",
"Keys":[
{"Key":{"Name":"Email Address","Value": emailAddress}}]
};
var uo = {
SaveOptions: [{"SaveOption":{PropertyName:"DataExtensionObject",SaveAction:"Delete"}}]
};
const soapClient = new FuelSoap(options);
//again, I don't control the structure of the next call.
let res = soapClient.delete('DataExtensionObject', co, uo, async function( err, response ) {
if ( err ) {
// I can get here, but my reject, or if I use return, does nothing
reject();
}else{
// I can get here, but my reject, or if I use return, does nothing
resolve();
}
});
console.log("res value " + res); // undefined - of course
}catch(err){
console.log("ALERT: Bad response back for removeFromDNC for email: " + emailAddress + " error: " + err);
console.log("removeFromDNC promise fulfilled in catch");
reject();
}
});
}
Both methods resolve and reject expect parameters, which are res and err in your case.
As far as removeFromDNC returns a Promise instance, you should call it using either async/await syntax:
const res = await removeFromDNC(...);
or chaining then/catch calls:
removeFromDNC(...)
.then((res) => { ... }) // resolve
.catch((err) => { ... }) // reject
EDIT:
If you want to avoid usage of callbacks inside removeFromDNC, consider promisifying of soapClient.delete call. Refer to util.promisify() if you working in Node.js or use own implementation.
Here is the example for demonstration:
const promisify = (fun) => (...args) => {
return new Promise((resolve, reject) => {
fun(...args, (err, result) => {
if(err) reject(err);
else resolve(result);
})
})
}
const soapClient = {
delete: (value, cb) => {
setTimeout(() => cb(null, value), 10);
}
};
async function removeFromDNC(emailAddress, accessToken_in) {
const soapDelete = promisify(soapClient.delete.bind(soapClient));
const res = await soapDelete('Soap Responce');
//You can use res here
return res;
}
removeFromDNC().then(res => console.log(res))

Await/Async is not waiting for promise [duplicate]

When using a simple callback such as in the example below:
test() {
api.on( 'someEvent', function( response ) {
return response;
});
}
How can the function be changed to use async / await? Specifically, assuming 'someEvent' is guaranteed to be called once and only once, I'd like the function test to be an async function which does not return until the callback is executed such as:
async test() {
return await api.on( 'someEvent' );
}
async/await is not magic. An async function is a function that can unwrap Promises for you, so you'll need api.on() to return a Promise for that to work. Something like this:
function apiOn(event) {
return new Promise(resolve => {
api.on(event, response => resolve(response));
});
}
Then
async function test() {
return await apiOn( 'someEvent' ); // await is actually optional here
// you'd return a Promise either way.
}
But that's a lie too, because async functions also return Promises themselves, so you aren't going to actually get the value out of test(), but rather, a Promise for a value, which you can use like so:
async function whatever() {
// snip
const response = await test();
// use response here
// snip
}
It's annoying that there isn't a straightforward solution, and wrapping return new Promise(...) is fugly, but I have found an ok work-around using util.promisify (actually it also kinda does the same wrapping, just looks nicer).
function voidFunction(someArgs, callback) {
api.onActionwhichTakesTime(someMoreArgs, (response_we_need) => {
callback(null, response_we_need);
});
}
The above function does not return anything, yet. We can make it return a Promise of the response passed in callback by doing:
const util = require('util');
const asyncFunction = util.promisify(voidFunction);
Now we can actually await the callback.
async function test() {
return await asyncFunction(args);
}
Some rules when using util.promisify
The callback must be the last argument of the function that is gonna be promisify
The supposed-callback must be in the form (err, res) => {...}
Funny thing is we do not need to ever specifically write what's the callback actually is.
async/await is magic. You can create a function asPromise to handle this kind of situations:
function asPromise(context, callbackFunction, ...args) {
return new Promise((resolve, reject) => {
args.push((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
if (context) {
callbackFunction.call(context, ...args);
} else {
callbackFunction(...args);
}
});
}
and then use it when you want:
async test() {
return await this.asPromise(this, api.on, 'someEvent');
}
the number of args is variable.
You can achieve this without callbacks , use promise async await instead of callbacks here how I would do this. And also here I have illustrated two methods to handle errors
clickMe = async (value) => {
// begin to wait till the message gets here;
let {message, error} = await getMessage(value);
// if error is not null
if(error)
return console.log('error occured ' + error);
return console.log('message ' + message);
}
getMessage = (value) => {
//returning a promise
return new Promise((resolve, reject) => {
setTimeout(() => {
// if passed value is 1 then it is a success
if(value == 1){
resolve({message: "**success**", error: null});
}else if (value == 2){
resolve({message: null, error: "**error**"});
}
}, 1000);
});
}
clickWithTryCatch = async (value) => {
try{
//since promise reject in getMessage2
let message = await getMessage2(value);
console.log('message is ' + message);
}catch(e){
//catching rejects from the promise
console.log('error captured ' + e);
}
}
getMessage2 = (value) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value == 1)
resolve('**success**');
else if(value == 2)
reject('**error**');
}, 1000);
});
}
<input type='button' value='click to trigger for a value' onclick='clickMe(1)' />
<br/>
<input type='button' value='click to trigger an error' onclick='clickMe(2)' />
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(1)'/>
<br/>
<input type='button' value='handling errors with try catch' onclick='clickWithTryCatch(2)'/>
const getprice = async () => {
return await new Promise((resolve, reject) => {
binance.prices('NEOUSDT', (error, ticker) => {
if (error) {
reject(error)
} else {
resolve(ticker);
}
});
})}
router.get('/binanceapi/price', async function (req, res, next) {
res.send(await binanceAPI.getprice());});

Chain promises calls

I need a consecutive execution of these three
return App.initAccount().then(App.bindEvents).then(App.render);
whereas App.initAccount():
initAccount: async function() {
console.log('Initializing account...');
await web3.eth.getCoinbase(function(err, account) {
if (err !== null) {
throw err;
}
App.account = account;
console.log('Account: ' + App.account);
});
}
and App.bindEvents(), App.render() just ordinary synchronous functions.
But what I've got in a console:
Initializing account...
Binding events
Rendering
Account: ***
Please, explain, how to chain these calls in order to wait until the asynchronous function is returned
UPD. According to the documentation I thought web3.eth.getCoinbase() returns a Promise<\String>, but turned out that wasn't correct.
I suppose web3.eth.getCoinbase() does not return a promise (so you are not awaiting anything here. You need to wrap the function call in a promise like this :
await new Promise((resolve, reject) => {
web3.eth.getCoinbase(function(err, account) {
if (err !== null) {
return reject(err)
}
App.account = account;
return resolve(account)
});
})
Edit based on T.J suggestion
In this case, you don't actually need initAccount to be an asynchronous function, you can refactor your function to be :
initAccount: function() {
return new Promise((resolve, reject) => {
console.log('Initializing account...');
web3.eth.getCoinbase(function(err, account) {
if (err !== null) {
return reject(err)
}
App.account = account;
console.log('Account: ' + App.account);
return resolve(account)
});
})
}
From a style standpoint, you're best off not mixing callbacks and async/await in terms of sequential flow. To use callback style functions and weave them in with async/await, you can wrap them in promises to "hide" the promises away:
class Foo {
initAccount() {
return new Promise((resolve, reject) => {
web3.eth.getCoinbase(function(err, account) {
if (err !== null) {
reject(err);
}
resolve(account);
});
});
}
}
const o = new Foo();
const account = await foo.initAccount();
// Then do bindEvents
// etc
getCoinbase does not look like a function returning a promise. You should wrap it to behave as expected.
Either manually:
await new Promise((resolve, reject) => {
web3.eth.getCoinbase(function(err, account) {
if (err !== null) {
reject(err);
}
App.account = account;
console.log('Account: ' + App.account);
resolve(account);
});
});
Or using something like this
I suspect web3.eth.getCoinbase doesn't return a promise, since you're passing a callback to it. await only awaits promises.
So you probably need to wrap that API in a promise, which means that initAccount wouldn't need to be an async function as this is one of the rare situations where you need new Promise:
initAccount: function() {
return new Promise((resolve, reject) => {
console.log('Initializing account...');
web3.eth.getCoinbase(function(err, account) {
if (err !== null) {
reject(err);
} else {
App.account = account;
console.log('Account: ' + App.account);
}
});
});
}
Separately, if bindEvents and render do their work synchronously, you'd usually put them in the same then handler:
return App.initAccount().then(() => {
App.bindEvents();
App.render();
});

Node.js using multiple promises

I currently try to use two kind of promises. One for the overall convensation, and one to get the userids to usernames.
The code works when its ran first time, and is looking something like this:
The output above is what i wanted.
But when i run the query again, i get this:
what is wrong with my code? what do i need to do to make the promises go correctly?
code:
function getconvensations(userid) {
return new Promise(function(resolve, reject) {
convensations.find({members: userid}).lean().then(function (convensations) {
var promises = convensations.map(function (currentConvensation) {
return new Promise(function (resolve, reject) {
var realnames = [];
var usernames = currentConvensation.members.map(function (CurrentUser) {
Core.userDetails(CurrentUser).then(function (user) {
realnames.push(user.username);
console.log("RESOLVING USERNAMES");
resolve();
});
});
Promise.all(usernames).then(function () {
console.log("GET LATEST MESSAGE");
latestMessage(currentConvensation._id).then(function (message) {
console.log("Messages: ");
console.log(message);
currentConvensation.spesificmessage = message;
currentConvensation.users = realnames;
currentConvensation.otherend = (currentConvensation.members[0] == userid ? realnames[0] : realnames[1]);
console.log("RESOLVE LATEST MESSAGE");
resolve();
});
});
});
});
Promise.all(promises).then(function () {
console.log("FINISHED CONVENSATIONS");
console.log("-------------------------");
console.log(convensations);
console.log("-------------------------");
return resolve(convensations);
}).catch(console.error);
});
});
}
caller:
} else if(action == 'getconvensations') {
Messages_model.getconvensations(req.user._id).then(function (response) {
res.json(response);
});
}
You have a race condition here:
new Promise(function (resolve, reject) {
…
currentConvensation.members.map(function (CurrentUser) {
Core.userDetails(CurrentUser).then(function (user) {
resolve();
});
});
…
latestMessage(currentConvensation._id).then(function (message) {
resolve();
});
…
});
There are arbitrarily many tasks executing concurrently, and the first promise that fulfills will call resolve().
The solution is to avoid the Promise constructor antipattern and never ever call new Promise or resolve manually! Instead, chain promise callbacks to each other using the then method, and return new promises from every function to allow the caller to wait for them.
function getconvensations(userid) {
return convensations.find({members: userid}).lean().then(function (convensations) {
// ^^^^^^
var promises = convensations.map(function (currentConvensation) {
var usernamepromises = currentConvensation.members.map(function (CurrentUser) {
console.log("GETTING USERNAME");
return Core.userDetails(CurrentUser).then(function (user) {
// ^^^^^^
console.log("FULFILLED USERNAME");
return user.username;
// ^^^^^^
});
});
return Promise.all(usernamepromises).then(function (realnames) {
// ^^^^^^
console.log("FULFILLED ALL USERNAMES");
currentConvensation.users = realnames;
currentConvensation.otherend = (currentConvensation.members[0] == userid ? realnames[0] : realnames[1]);
console.log("GETTING LATEST MESSAGE");
return latestMessage(currentConvensation._id);
// ^^^^^^
}).then(function (message) {
console.log("FULFILLED LATEST MESSAGE");
console.log("Message: ", message);
currentConvensation.spesificmessage = message;
});
});
return Promise.all(promises).then(function () {
// ^^^^^^
console.log("FINISHED ALL CONVENSATIONS");
console.log("-------------------------");
console.log(convensations);
console.log("-------------------------");
return convensations;
// ^^^^^^
});
});
}

Categories

Resources