How can I convert the following JavaScript Promise into regular asychronous code? - javascript

Hi I'm working with the following code snippet:
imaps.connect(config).then(function (connection) {
connection.openBox('INBOX').then(function () {
// Fetch emails from the last 24h
var delay = 24 * 3600 * 1000;
var yesterday = new Date();
yesterday.setTime(Date.now() - delay);
yesterday = yesterday.toISOString();
var searchCriteria = ['UNSEEN', ['SINCE', yesterday]];
var fetchOptions = { bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)'], struct: true };
// retrieve only the headers of the messages
return connection.search(searchCriteria, fetchOptions);
}).then(function (messages) {
var attachments = [];
messages.forEach(function (message) {
var parts = imaps.getParts(message.attributes.struct);
attachments = attachments.concat(parts.filter(function (part) {
return part.disposition && part.disposition.type.toUpperCase() === 'ATTACHMENT';
}).map(function (part) {
// retrieve the attachments only of the messages with attachments
return connection.getPartData(message, part)
.then(function (partData) {
return {
filename: part.disposition.params.filename,
data: partData
};
});
}));
});
return Promise.all(attachments);
}).then(function (attachments) {
console.log(attachments);
// =>
// [ { filename: 'cats.jpg', data: Buffer() },
// { filename: 'pay-stub.pdf', data: Buffer() } ]
});
I'm trying to remove Promises and turn the code into code using callbacks.
At the moment, I'm looping through all the attachments and when I print an individual attachment to the console I get:
Promise { <pending> }
How can I convert the code to regular callback code such that I can access the 'filename' and 'data' attributes of the attachment?

Actually, I don't understand why you need to go back from a promise to a callback but you can use the following code. It just a promise converter:
const promiseToCallback = function (promise) {
if (!isFn(promise.then)) {
throw new TypeError('Expected a promise');
}
return function (cb) {
promise.then(function (data) {
setImmediate(cb, null, data);
}, function (err) {
setImmediate(cb, err);
});
};
};
To use it
promiseToCallback(promise)(function(err, data) {
...
});

Related

API GET request returns undefined in Node JS

I'm new to Node JS and still figuring out how API GET requests are working. I'm trying to call API GET request using Node Js, function is supposed to get data from the servers and url will be "url/books?name=" where "name" will be passed to the function from input field. So far I have this function
async function getBooksInfo(name) {
const url = `https://test/books?name=${name}`;
return new Promise(function (resolve, reject) {
https.get(url, res => {
res.setEncoding("utf8");
let body = "";
res.on("data", data => {
body += data;
});
res.on("end", () => {
body = JSON.parse(body);
resolve(body)
});
res.on("error", (e) => {
reject(e);
});
});
})
}
and another function will create stream of inputs,
async function storeInfo() {
const date = readLine().trim();
const result = await getBooksInfo(date);
const isEmpty = !Object.keys(result).length;
if (isEmpty) {
ws.write('We don't have this book in our database');
} else {
ws.write(`Book Name: ${result.bookName}`);
ws.write(`Year: ${result.year}\n`)
}
}
but for some reason stream of inputs return undefined, I don't seems to understand what could be the issue. Any help and suggestion is greatly appreciated.
Updated: this is what console.log shows
{
page: 1,
per_page: 100,
total_pages: 20,
data: [
{
bookName: 'test',
year: 1975,
author: "test,
}
]
}

Ending imap-simple connection within recursion

I currently have a script that checks for an incoming email (in a mailbox) every 30 seconds, using a recursion.
The package I'm using for this testing is imap-simple.
The below script currently does this as required;
var imaps = require('imap-simple');
const { connect } = require('net');
var config = {
imap: {
user: 'qatestspecialist#outlook.com',
password: 'specialistQa',
host: 'imap-mail.outlook.com',
port: 993,
tls: true,
authTimeout: 30000
}
};
module.exports = {
'delete any existing emails...': function () {
imaps.connect(config).then(function (connection) {
connection.openBox('INBOX').then(function () {
var searchCriteria = ['ALL'];
var fetchOptions = { bodies: ['TEXT'], struct: true };
return connection.search(searchCriteria, fetchOptions);
//Loop over each message
}).then(function (messages) {
let taskList = messages.map(function (message) {
return new Promise((res, rej) => {
var parts = imaps.getParts(message.attributes.struct);
parts.map(function (part) {
return connection.getPartData(message, part)
.then(function (partData) {
//Display e-mail body
if (part.disposition == null && part.encoding != "base64"){
console.log(partData);
}
//Mark message for deletion
connection.addFlags(message.attributes.uid, "\Deleted", (err) => {
if (err){
console.log('Problem marking message for deletion');
rej(err);
}
res(); //Final resolve
});
});
});
});
});
return Promise.all(taskList).then(() => {
connection.imap.closeBox(true, (err) => { //Pass in false to avoid delete-flagged messages being removed
if (err){
console.log(err);
}
});
connection.end();
});
});
});
},
'send email to seller and wait for mailbox notification': function (browser) {
// script to send an email to the mailbox...
},
'get new email info': function(browser) {
const createPromise = ms => new Promise((resolve, reject) => {
setTimeout(() => resolve(ms), ms)
});
function findUnseenEmails(connection) {
return connection.openBox('INBOX').then(function () {
var searchCriteria = ['UNSEEN'];
var fetchOptions = {
bodies: ['HEADER', 'TEXT'],
markSeen: false
};
return connection.search(searchCriteria, fetchOptions).then(function (results) {
var subjects = results.map(function (res) {
return res.parts.filter(function (part) {
return part.which === 'HEADER';
})
[0].body.subject[0];
});
return subjects.length > 0 ? subjects : createPromise(30000).then(function() { return findUnseenEmails(connection);
});
});
});
}
imaps.connect(config).then(function (connection) {
return findUnseenEmails(connection)
})
.then((subjects) => console.log(JSON.stringify(subjects)));
},
'Closing the browser': function (browser) {
browser.browserEnd();
}
};
This waits for an email and then displays the email 'header'.
However, the imap connection does not close, and stays open which is stopping my test suite from completing as the associated test never actually finishes.
I've tried adding the imap-simple command connection.end() in several places after the
imaps.connect(config).then(function (connection) {
return findUnseenEmails(connection)
})
part of the script, but it doesn't work.
So I'm just wondering if anyone knows where I should be adding this connection.end() command in order for the connection to be closed once an email has been received?
Any help would be greatly appreciated.
This has now been resolved in another post, using the following;
if (subjects.length > 0) {
connection.end();
return subjects;
} else {
return createPromise(5000).then(function() { return findUnseenEmails(connection)});
}

function not returning with promise

I'm trying to call a function (refresh_access_token) from another one and create a Promise chain from that. But the return function inside refresh_access_token is not working. The refresh_access_token doesn't return to it's caller when it completes.
I'm receiving this message:
Unhandled rejection TypeError: Cannot read property 'then' of undefined
How can I fix this?
These are the 2 function code:
exports.refresh_access_token = function(environment_hash) {
var MercadoLibre = require('../../models/index').MercadoLibre;
var needle = require('needle');
const load = require('express-load');
var meli = require('mercadolibre');
var request=require('request');
const mysql = require('mysql');
const dotenv = require('dotenv').config({path: '../../.env'});
var oauth_url = 'https://api.mercadolibre.com/oauth/token';
var env_hash = environment_hash;
where = { environment_hash: env_hash };
MercadoLibre.findOne({where: where}).then(function (account) {
if (!account) {
// Item not found
} else {
// Found an item, update it
needle.post(oauth_url, {
grant_type: 'refresh_token',
client_id: process.env.MERCADOLIBRE_CLIENT_ID,
client_secret: process.env.MERCADOLIBRE_SECRET,
refresh_token: account.refresh_token
}, {
}, function (err, res, body) {
if (body) {
expires_in = new Date();
expires_in.setUTCHours(0);
expires_in.setTime(expires_in.getTime() + 21600*1000);
values = {
refresh_token: body.refresh_token,
expires_in: expires_in
};
where = { environment_hash: env_hash };
return MercadoLibre.update(values, {where: where});
}
});
}
});
}
exports.run_mercadolibre_jobs = function() {
var MercadoLibre = require('../../models/index').MercadoLibre;
var values = {
attributes: ['environment_hash'],
raw: true
};
MercadoLibre
.findAll(values)
.then(function(accounts) {
Promise.all(accounts.map(function(account) {
module.exports.refresh_access_token(account.environment_hash)
.then(function(response) {
console.log(response);
})
.catch(function(response){
console.log(response);
});
}));
});
}
Your function refresh_access_token is not returning anything. Your only return statement is inside the needle.post callback. You should be first returning:
return MercadoLibre.findOne(...);
However, you are mixing promises, with callbacks (used in your needle.port call). I would suggest reviewing Promises vs Callbacks and how to use them together. Here is a good thread on how to convert callback-apis to promises: How do I convert an existing callback API to promises?.
Another alternative would be to replace using needle with a promise-supported node library:
Axios
Request Promise

Chaining function calls using promises

Hi Im trying to call subsequent functions of the same types passing parameters as shown in the example:
The problem is that im getting random output like, Line3, Line4, Line1, Line2. Am I doing something wrong with the chaining of is it a problem with the latency of the rendering of each text to screen? Should i use npm sleep?
var function1 = (senderID,req,FB_ID) => {
return new Promise((resolve,reject) => {
var line1 = 'Line1';
var line2 = 'Line2';
var line3 = 'Line3';
var line4 = 'Line4';
// Display lines of text
sendTextMessage(senderID,line1)
.then(sendTextMessage(senderID,line2))
.then(sendTextMessage(senderID,line3))
.then(sendTextMessage(senderID,line4))
.then(resolve());
});
};
Each sendTextMessage call calls this function below:
var sendTextMessage = (recipientId, messageText) => {
return new Promise((resolve,reject) => {
var messageData = {
recipient: {
id: recipientId
},
message: {
text: messageText
}
};
callSendAPI(messageData).then(function(){
resolve();
});
});
};
CALLSENDAPI :
var callSendAPI = (messageData) => {
var sleepSec = 1;
return new Promise((resolve,reject) => {
request({
uri: 'https://graph.facebook.com/v2.6/me/messages',
qs: { access_token: process.env.FB_PAGE_TOKEN },
method: 'POST',
json: messageData
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
//var recipientId = body.recipient_id;
//var messageId = body.message_id;
sleep.sleep(sleepSec);
resolve();
} else {
console.error("Unable to send message.");
console.error(response);
console.error(error);
reject(error);
}
});
});
}; // func
The problem is that what you have put as then argument gets executed immediately, while instead you should pass a function.
You could use bind to avoid overly verbose anonymous functions:
sendTextMessage(senderID,line1)
.then(sendTextMessage.bind(null, senderID,line2))
.then(sendTextMessage.bind(null, senderID,line3))
.then(sendTextMessage.bind(null, senderID,line4))
.then(resolve);
Also, note the resolve at the end, without parentheses: you want to pass that function, not execute it.

Use ldapjs with promise

I want to convert the following code to use promise. It is working and output a user's attributes within the active directory.
var client = ldap.createClient({
url: ldap_url
});
client.bind(ldap_username, ldap_password, function (err) {
client.search(ldap_dn_search, opts, function (err, search) {
search.on('searchEntry', function (entry) {
var user = entry.object;
// It is working!!!. It outputs all user attributes.
console.log(user);
});
});
});
The following is my attempt, butit doesn't output anything.
var Promise = require('promise');
var client_bind = Promise.denodeify(client.bind);
var client_search = Promise.denodeify(client.search);
client_bind(ldap_username, ldap_password)
.then(function(err){
client_search(ldap_dn_search, opts)
.then(function(search){
var search_on = Promise.denodeify(search.on);
search_on('searchEntry')
.then(function(entry){
var user = entry.object;
// It doesn't output anything !!!
console.log(user);
});
});
});
I had the same problem.
Search emits events, so we need something that processes them and passes further along the chain.
Here is piece of code, that works for me:
var ldap = require('ldapjs');
var promise = require('bluebird');
var client = ldap.createClient({url: app.settings['ldap']['server']});
var uid;
promise.promisifyAll(client);
function searchPromise(res, notfoundtext) {
return new Promise(function(resolve, reject) {
var found = false;
res.on('searchEntry', function(entry) {
found = true;
resolve(entry);
});
res.on('error', function(e) {
reject(e.message);
});
res.on('end', function() {
if (!found) {
reject(notfoundtext);
}
});
});
}
client.searchAsync(app.settings['ldap']['baseDn'], {filter: '(mail='+credentials.email+')', scope: 'sub'})
.then(function(res) {
return searchPromise(res, 'User isn\'t exists.');
})
.then(function (entry) {
uid = entry.object.uid;
return client.bindAsync(entry.object.dn, credentials.password);
})
.then(function() {
return client.searchAsync('cn='+app.settings['ldap']['group']+',cn=groups,'+app.settings['ldap']['baseDn'], {scope: 'sub', filter: '(memberUid='+uid+')'});
})
.then(function(res) {
return searchPromise(res, 'User is not in group ' + app.settings['ldap']['group']);
})
.then(function() {
console.log('All is ok');
})
.catch(function(message) {
console.log('Error:' + message);
});
Immediately after the search I add one more step that catches the events, processes them, and passes it further along the chain. This makes the function searchPromise.
Good luck coding )
Most likely those methods do require to be called on client as a context, so you will need to bind() them before passing them to Promise.denodeify:
var client_bind = Promise.denodeify(client.bind.bind(client));
var client_search = Promise.denodeify(client.search.bind(client));
Also, a proper use of promises would look like this:
client_bind(ldap_username, ldap_password).then(function() {
return client_search(ldap_dn_search, opts);
// ^^^^^^ always return something from the callback
}).then(function(search) { // flatten your chain
return Promise.denodeify(search.on).call(search, 'searchEntry');
// ^^^^^^ an alternative to `bind`
}).then(function(entry){
var user = entry.object;
console.log(user);
}).catch(function(err) { // always catch errors!
console.error(err);
});
Using Bluebird Promises, the easy way to do this is to create your client normally, and then run the promisifyAll() on the client.
var ldap = require('ldapjs');
var Promise = require('bluebird');
var client = ldap.createClient({
url: 'ldap://my-server:1234',
});
Promise.promisifyAll(client);
Now you can call client.addAsync() and client.searchAsync() and such.
client.bindAsync(secUserDn, secUserPassword)
.then(doSearch) // if it works, call doSearch
.catch(function (err) { // if bind fails, handle it
console.error('Error on bind', err)
});
function doSearch(data) {
client.searchAsync('CN=A Test,OU=Users,DC=website,DC=com', options)
.then(function (data) { // Handle the search result processing
console.log('I got a result');
})
.catch(function (err) { // Catch potential errors and handle them
console.error('Error on search', err);
});
}
i had the same issue here but i solved it by adding promise and resolve the response without using bluebird, this is an exemple of my code :
async getLdapUser(username: any): Promise<any> {
let myPromise = new Promise<boolean>((resolve, reject) => {
console.log('ssssssssss', username);
const adSuffix = 'OU=xxxx,OU=xxxxx,DC=xxxxxxx,DC=xxxxxx';
const password = 'xxxxxxxxxxxxx';
// Create client and bind to AD
const client = ldap.createClient({
url: 'ldap://1.1.1.1:389',
});
// promise.promisifyAll(client);
let resp = false;
// console.log(client);
client.bind('userTest', password,(err: any) => {
console.log('RESP', resp);
if (err) {
console.log('Error in new connetion ' + err);
} else {
/*if connection is success then go for any operation*/
console.log('Success');
const searchOptions: {} = {
scope: 'sub',
filter: '(sAMAccountName=' + username + ')',
attributes: ['sAMAccountName'],
};
client.search(adSuffix, searchOptions, (err: any, res: any) => {
assert.ifError(err);
res.on('searchEntry', (entry: any) => {
resp = true;
});
res.on('error', (error: any) => {
console.log('err');
reject(error.message);
});
await res.on('end', (result: any) => {
resolve(resp);
});
});
}
});
});
return myPromise;
}

Categories