I am really stuck with passing values between promises, I wonder if I can get a help here.
I have a CustomerController calling methods in CustomerRepo
let getCustomers = customerRepo.getCustomers(req.query.offset, req.query.return);
let getProfiles = customerRepo.getProfiles(rows);
getCustomers
.then(function (rows) {
console.log(rows[0]);
})
.then(getProfiles)
I do not know how to pass rows (array of customers) to getProfiles so I can populate the customers object with profiles.
Here is the code in CustomerRepo -
var getCustomers = function (offset, _return) {
return new Promise(function (resolve, reject) {
var sqlString = 'call qsel_customers' + '(' + offset + ',' + _return + ')';
pool.getConnection(function (err, connection) {
if (err) {
return reject(err);
}
connection.query(sqlString, function (err, rows) {
if (err) {
reject(err);
}
else
resolve(rows);
connection.release();
});
connection.on('error', function (err) {
res.json({"code": 100, "status": "Error in connection database"});
});
});
});
}
and for getProfiles
var getProfiles = function (rows) {
return new Promise(function (resolve, reject) {
console.log(rows);
}
}
I get rows undefined. Also please could someone suggest how can I extract the db mysql connection code into a dbConnect.js, to avoid code repetition.
Promise denotes the computation that may fail or not at some point in time. Therefore, in order to create a Promise you do:
return new Promise(function(resolve, reject) {
// here you decide when the computation fails and when succeeds
resolve(1) // success
reject('error') // failed
})
Promise has a method (a swiss-knife, indeed) called then. It takes a function ƒ of one argument. Now, the magic of ƒ looks like this:
if ƒ returns simple value (string, number, array, etc) then it will wrap it in a new Promise and return it. The signature is then : Promise[a] -> (a -> b) -> Promise[b]. Therefore, say you have a function ∂ that returns a Promise of a number and ƒ takes a number and adds "!" to it, you and get this number, pass it to ƒ using then and end up with a new Promise:
ƒ.then(n => n + '!') // Promise[String]
if ƒ returns a Promise, then then will "extract" the value if this Promise, pass it to ∂ and return a new Promise with the result. In pseudocode:
ƒ.then(n => Promise(n + '!')) // Promise[String]
Okay, then returns a Promise. Well, let's design the code:
let getCustomers = () => Promise.resolve([ {}, {}, {} ])
// assume myApi.getProfile takes a Customer and returns a Profile
let getProfiles = (customers) => customers.map(myApi.getProfile)
getCustomers().then(getProfiles).then(console.log.bind(console));
I'm no expert in Promises but i don't think that it is good idea to put pool.getConnection inside your getCustomers promise.
It looks like another promise and should be chained rather then combined.
Getting back to your main quersion. You can do this
getCustomers
.then(function (rows) {
return getProfiles(rows);
})
.then(function() {
console.log('get profiles resolved');
});
and
var getProfiles = function (rows) {
return new Promise(function (resolve, reject) {
resolve(rows);
console.log('other log of rows:' + rows);
}
}
EDITED:
let getCustomers = customerRepo.getCustomers(req.query.offset, req.query.return);
let getProfiles = customerRepo.getProfiles;
getCustomers
.then(function (rows) {
return getProfiles(rows);
})
.then(function(rows) {
console.log('rows number ' + rows);
console.log('get profiles resolved');
);
When chaining Promises, you have to return a Promise resolved with the value you're passing from the upstream function in order to use it in the downstream function.
getCustomers
.then(function (rows) {
console.log(rows[0]);
return Promise.resolve(rows[0]);
})
.then(function(firstRow) {
console.log("The first row's id is " + firstRow.id);
return Promise.resolve(firstRow);
})
.then(getProfiles);
If you need to do asynchronous work in one of the functions, return a new Promise and invoke the resolve/reject functions as appropriate in the asynchronous callback. Promise.resolve() is a shortcut you can use if you're not doing anything asynchronously; it simply returns a Promise object resolved with the value it's passed.
Related
EDIT
So I am creating an e-commerce website and I'm running into a few road blocks. I have since updated my code to make it cleaner and easier to read and I somewhat pinpointed where my issue stems from (though there could be more). Here is the updated code:
StoreDB.prototype.addOrder = function(order) {
console.log('addOrder');
return this.connected.then(function(db) {
console.log(order);
var orders = db.collection('orders');
var products = db.collection('products');
return insert(order, orders, db)
.then(function(updatedOrdersList) {
return obtainOrders(updatedOrdersList);
})
.then(function(orderList) {
return updateProducts(orderList, products);
})
.catch((err) => console.log('There was an issue in the promise chain\n' + err));
});
}
function insert(order, orders, db) {
return new Promise(function(resolve, reject) {
console.log('Inserting order into orders collection');
orders.insert(order);
resolve(db.collection('orders'));
});
}
function obtainOrders(orders) {
return new Promise(function(resolve, reject) {
console.log('Obtaining orders');
var orderList = orders.find({}).toArray();
resolve(orderList);
});
}
function updateProducts(orderList, products) {
return new Promise(function(resolve, reject) {
console.log('Getting the products that match the cart')
var promises = [];
var promises2 = [];
console.log('orderList', orderList);
for (var item in orderList[0].cart) {
console.log('item', item);
promises.push(products.find({$and : [{"_id" : item}, {"quantity" : {$gte: 0}}]}).toArray());
}
Promise.all(promises)
.then(() => {
console.log('Updating the cart quantity')
for (var i = 0; i < promises.length; i++) {
console.log(promises);
// console.log(productList[item], productList[item]._id, productList[item].quantity, cart);
promises2.push(products.update({"_id" : productList[item]._id}, {$set :{"quantity" : (productList[item].quantity - cart[productList[item]._id])}}));
promises2.push(products.find({}).toArray());
Promise.all(promises2)
.then(() => promises[promises2.length-1]);
}
});
});
}
It appears that when obtainOrders is called in the promise chain, it returns an empty array. I'm not too sure why it does this because in the chain where:
return insert(order, orders, db)
.then(function(updatedOrdersList) {
return obtainOrders(updatedOrdersList);
})
.then(function(orderList) {
return updateProducts(orderList, products);
})
The function call to obtainOrders from above waits for the method call to Collections.find to be completed before it is resolved (returning an array of orders). Some reason it is returning an empty array.
Any help would be greatly appreciated!
Not sure what the relationship between the first block and second is, but in the first block:
function insert(order, orders, db) {
return new Promise(function(resolve, reject) {
console.log('Inserting order into orders collection');
orders.insert(order);
resolve(db.collection('orders'));
});
}
At a glance it seems like orders.insert(order) is asynchronous, but you're not waiting for it to finish before resolving.
You could either make the function asynchronous and
await orders.insert(order);
or
orders.insert(order)
.then(() => resolve(db.collection('orders')))
But doesn't having the method Collections.insert inside a promise make that method call asynchronous?
Yes, Collections.insert is implicitly asynchronous in that it returns a Promise. I was talking about declaring it async explicitly so you can await other async calls in the function body. Note the addition of async at the start and await orders.insert().
async function insert(order, orders, db) {
return new Promise(function(resolve, reject) {
console.log('Inserting order into orders collection');
await orders.insert(order);
resolve(db.collection('orders'));
});
}
If you don't await orders.insert (either via await or .then) you're calling insert and immediately returning the db.collection call, which creates a race condition where the new order may not be inserted yet. It's possible the new order could be there but it seems unlikely, and it's not going to be there reliably.
There's also no need to create a new Promise here. db.collection already returns a Promise. Just return the one you already have:
function insert(order, orders, db) {
console.log('Inserting order into orders collection');
return orders.insert(order)
.then(() => db.collection('orders'));
}
Take a look at this sample code snippet. I've mocked out the orders.insert and db.collection methods.
// mock orders.insert
const orders = {
insert: async() => {
return new Promise((resolve) => {
// wait a second then resolve
setTimeout(resolve, 1000);
});
}
}
// mock db.collection
const db = {
collection: async() => {
return new Promise((resolve) => {
// wait a second then resolve with mock data
setTimeout(() => resolve(['foo', 'bar', 'baz']), 1000);
});
}
}
function insert(order, orders, db) {
return orders.insert(order)
.then(() => db.collection('orders'));
}
// same as above, but with async/await
async function insertAsync(order, orders, db) {
await orders.insert(order);
return await db.collection('orders');
}
function go() {
// using then
insert(null, orders, db).then(console.log);
// using async/await;
insertAsync(null, orders, db).then(console.log);
}
go();
In JavaScript/Node.js, how to use Async/Await for the below code to return placeName with a,b,c & d.
test.csv is having c,d
var placeName = ["a","b"];
var csvFile = 'test.csv';
fs.readFile(csvFile, 'UTF-8', function(err, csv) {
$.csv.toArrays(csv, {}, function(err, data) {
for(var i=0, len=data.length; i<len; i++) {
console.log(data[i]); //Will print every csv line as a newline
placeName.push(data[i][0].toString());
}
});
console.log(placeName); //Inside the function the array show a,b,c,d
});
// Prints only a and b. Did not add c & d from the csv.
console.log(placeName);
Create a function returning a promise
The await operator waits for a promise to be fulfilled and can only be used within async functions - which return a promise when called. So updating place names will need to be written as a function that returns a promise. Using a single promise for all operations is possible but not recommended:
function addNames(placeName, csvFile) {
return new Promise( (resolve, reject) => {
fs.readFile(csvFile, 'UTF-8', function(err, csv) {
if(err) {
reject(err);
}
else {
$.csv.toArrays(csv, {}, function(err, data) {
if( err) {
reject( err);
}
else {
for(var i=0, len=data.length; i<len; i++) {
console.log(data[i]); //print supplied data elements
placeName.push(data[i][0].toString());
}
}
resolve( placeName);
});
}
});
});
}
Although untested, this is based on the posted code: the input placeName array is modified in place. If fulfilled, the promise value is set to the modified array. However ....
Reducing indented code
Notice the code becomes more indented the more node style callback operations are added? Taken to extreme this can result in a condition known as "call back hell" - difficult to write, compile without error or maintain.
Removing nested callbacks is one of the design aims of promises and can be achieved by promisifying operations separately. The following example separates not just reading and decoding, but transforming the decoded data and adding it to an existing array:
function readCsvFile( csvFile) {
return new Promise( (resolve, reject) => {
fs.readFile(csvFile, 'UTF-8',
(err, csv) => err ? reject(err) : resolve( csv)
);
});
}
function decodeCsv( csv) {
return new Promise( (resolve, reject) => {
$.csv.toArrays(csv, {},
(err, data) => err ? reject( err) : resolve( data)
);
});
}
function extractNames( data) {
return data.map( item => item[0].toString());
}
function addNames( placeName, csvFile) {
return readCsvFile( csvFile)
.then(decodeCsv)
.then(extractNames)
.then( // push new names to placeName and return placeName
names => names.reduce( (a,name) => {a.push(name);return a}, placeName)
);
}
Calling as part of a promise chain
Usage without using async/await requires adding promise handlers for fulfillment and rejection to be called asynchronously after data becomes available or an error occurs:
var placeName = ["a","b"];
var csvFile = 'test.csv';
addNames( placeName, csvFile)
.then( placeName => console.log(placeName)) // log updated places
.catch( err => console.log( err)); // or log the error.
Usage of await
As mentioned, the await operator can only be used inside async functions. It waits for fulfilled promise data so that it can be processed further inside the same function.
async function example() {
var placeName = ["a","b"];
var csvFile = 'test.csv';
await addNames( placeName, csvFile);
// do something with placeName
return somethingElse;
}
Adding fulfillment and rejection handlers to the promise returned by calling example has not been shown.
About return await ...
Note that a promise returned from an async function resolves the promise returned by calling the function. Hence the coding pattern of
return await operand
contains an unnecessary await operator.
Writing async functions instead of ordinary ones (TLDR)
Using async functions in node still requires converting operations which use error/success callbacks into requests that returns a promise ("promisifying" the operation), but syntactically allows writing code processing the request to be written immediately following await operations. Code produced is similar in style to procedural code. For example:
function readCsvFile( csvFile) {
return new Promise( (resolve, reject) => {
fs.readFile(csvFile, 'UTF-8',
(err, csv) => err ? reject(err) : resolve( csv)
);
});
}
function decodeCsv( csv) {
return new Promise( (resolve, reject) => {
$.csv.toArrays(csv, {},
(err, data) => err ? reject( err) : resolve( data)
);
});
}
async function addNames( placeName, csvFile) {
let csv = await readCsvFile( csvFile);
let arrays = await decodeCsv( csv);
let names = arrays.map( item => item[0].toString());
placeName.push.apply( placeName, names);
return placeName;
}
I am trying to create an array called pw_array, assign the contents of pw_subscribers to that array, then append each object in the pw_array with new key value pairs from the second promise. I am new to promises and having a lot of trouble making this work. Right now when I console.log(pw_customer) inside the second promise, inside the getCustomers function, it is returning what I want. But when I console.log(pw_array) later it is the original array.
var pw_array = [];
//first promise is working correctly
var getPaywhirlSubscribers = new Promise(function(resolve, reject) {
paywhirl.Subscriptions.getsubscribers({limit:100}, function(error, pw_subscribers) {
Promise.all(JSON.parse(pw_subscribers).forEach(function(pw_subscriber) {
pw_array.push(pw_subscriber);
}))
// console.log(pw_array);
return resolve(pw_array);
});
});
var getGatewayReferences = function(pw_array) {
return new Promise(function(resolve, reject) {
Promise.all(pw_array.forEach(function(pw_customer) {
paywhirl.Customers.getCustomer(pw_customer.customer_id, function(error, customer) {
pw_customer.phone = customer.phone;
pw_customer.address = customer.address;
pw_customer.gateway_reference = customer.gateway_reference;
// this console.log is returning what I want
// console.log(pw_customer);
});
}));
resolve(pw_array);
// console.log(pw_array);
});
};
and the promise chain...
getPaywhirlSubscribers.then(getGatewayReferences).then(function(pw_array) {
// this console.log is returning the original pw_array with pw_subscribers but not with the appended pw_customer keys
console.log(pw_array);
});
All of your code can be reduced to
var getPaywhirlSubscribers = function() {
return new Promise(function(res, rej) {
paywhirl.Subscriptions.getSubscribers({limit:100}, function(err, subs) {
if (err) {
rej(err);
} else {
res(JSON.parse(subs));
}
});
});
};
var gatewayRefs = function(promiseOfArray) {
return promiseOfArray.then(function(subs) {
return Promise.all(subs.map(function(sub) {
return new Promise(function(res, rej) {
paywhirl.Customers.getCustomer(sub.customer_id, function(err, customer) {
if (err) {
rej(err);
} else {
res(Object.assign({}, sub, customer);
}
});
});
});
});
};
gatewayRefs(getPaywhirlSubscribers()).then(function(arrayOfCustomers) {
// do something with the customer array
});
Note that you can make this even shorter/simpler if you use one of the many utilities available to automatically convert node.js-style error -first callback APIs into Promise-based ones. Search for 'promise denodeify'.
You could also conceivably pull some of the steps out into a .then chain to reduce nesting, but that's as much aesthetic as practical IMHO.
I got 4 promises here, and I thought that it would run the first one, then wait until its finished, THEN run the next one, wait till finished and THEN run the next one etc..
But what happens here is that it runs all of them all at once and does not wait for anything to finish.
This is my promise chain:
//
// Run the promises
//
findBanks
.then(findReceipts)
.then(findExpenses)
.then(sendResult)
.catch(err => {
console.error(err);
console.log("getbankAccountReport ERR: " + err);
res.json({error:true,err})
});
This is the output from my console.log
=====findAllBank=====
=====findAllReceipt=====
=====findAllExpense=====
=====RESOLVE findAllBank=====
=====sendResult=====
=====RESOLVE sendResult=====
=====RESOLVE findAllReceipt=====
=====RESOLVE findAllExpense=====
Am I not understanding promises correct or?
Anyway here is my nodejs controller:
exports.getBankAccountReport = function(req, res) {
//
// Find all bank accounts
//
var bankModel = require('../models/bankModel');
var bankTable = mongoose.model('bankModel');
var bankArray = [];
var findAllBank = new Promise(
(resolve, reject) => {
console.log("=====findAllBank=====")
bankTable.aggregate([
...lots of mongo stuff...
],function(err, data) {
if (!err) {
bankArray = data;
console.log("=====RESOLVE findAllBank=====")
resolve(data);
} else {
reject(new Error('findBank ERROR : ' + err));
}
});
});
//
// Find the RECEIPT for each bank account
//
var receiptModel = require('../models/receiptModel');
var receiptTable = mongoose.model('receiptModel');
var receiptArray = [];
var findAllReceipt = new Promise(
(resolve, reject) => {
console.log("=====findAllReceipt=====")
receiptTable.aggregate([
...lots of mongo stuff...
], function (err, data) {
if (!err) {
receiptArray = data;
console.log("=====RESOLVE findAllReceipt=====")
resolve(data);
} else {
reject(new Error('findReceipts ERROR : ' + err));
}
});
});
//
// Find the EXPENSE for each bank account
//
var expenseModel = require('../models/expenseModel');
var expenseTable = mongoose.model('expenseModel');
var expenseArray = [];
var findAllExpense = new Promise(
(resolve, reject) => {
console.log("=====findAllExpense=====")
expenseTable.aggregate([
...lots of mongo stuff...
], function (err, data) {
if (!err) {
expenseArray = data;
console.log("=====RESOLVE findAllExpense=====")
resolve(data);
} else {
reject(new Error('findExpense ERROR : ' + err));
}
});
});
var sendResult = function(data) {
var promise = new Promise(function(resolve, reject){
console.log("=====sendResult=====")
res.json({error:false,
"bank":bankArray,
"receipt":receiptArray,
"expense":expenseArray})
console.log("=====RESOLVE sendResult=====")
resolve();
});
return promise;
};
//
// Run the promises
//
findAllBank
.then(findAllReceipt)
.then(findAllExpense)
.then(sendResult)
.catch(err => {
console.error(err);
console.log("getbankAccountReport ERR: " + err);
res.json({error:true,err})
});
}
You need to wrap your Promises in functions
var findAllBank = function() {
return new Promise(
(resolve, reject) => {
console.log("=====findAllBank=====")
bankTable.aggregate([
...lots of mongo stuff...
],function(err, data) {
if (!err) {
bankArray = data;
console.log("=====RESOLVE findAllBank=====")
resolve(data);
} else {
reject(new Error('findBank ERROR : ' + err));
}
});
});
});
When resolved, the next function in the chain will be called with the data passed in the resolve() function.
Do no confuse the Promise and the function that builds it
When you create a new Promise(executor), you instanciate a new object that will have two methods (functions of an object), .then(resolveCB [, rejectCB]) and .catch(rejectCB).
The aime is to know, whenever a process is done, if it was successful or if it failed, and the continue accordingly.
var myFirstPromise = new Promise(function executor(resolve, reject) { resolve('resolved'); });
In other words, those methods are used to continue your process once the promise defined by executor is settled. It can either get fulfilled and call the resolveCB callback (usingthen), or rejected and call the rejectCBcallback (using both then and catch). A callback (resolveCB or rejectCB) is a function, not a Promise itself, even if the callback might return a Promise.
myFirstPromise
.then(function resolveCB(result) { console.log(result); }) //you can use a 2nd callback for rejection at this point
.catch(function rejectCB(err) { console.log(err); });
myFirstPromise
.then(
function resolveCB(result) { console.log(result); } // if resolved
, function rejectCB(err) { console.log(err); } // if rejected
)
.catch(function rejectCB(err) { console.log(err); }); // NEVER forget the last catch, just my 2cents :)
We saw the inputs of .then() and .catch() but what about their return value? The both of them will return a new Promise. That's why you can chain the .then()'s and .catch()'s.
myFirstPromise
.then(function resolveCB1(result) { console.log(result); })
.then(function resolveCB2(result) { console.log(result); }) // console.log is called but result is undefined
.catch(function rejectCB1(err) { console.log(err); });
myFirstPromise
.then(function resolveCB3(result) {
throw 'I threw an exception'; // an exception is thrown somewhere in the code
console.log(result);
})
.then(function resolveCB4(result) { console.log(result); })
.catch(function rejectCB2(err) { console.log(err); }); // a promise in the chain get rejected or error occured
In the previous example, we see that our second .then() is hit but result is undefined. The promise returned by first .then() fullfiled but no value as been passed by the executor to the resolve callback resolveCB2. In the second case, an exception occured in resolveCB3, it gets rejected so rejectCB2 is called. If we want our resolve callbacks to receive an argument, we have to notify the exector. To do so, the simplest way is for the callbacks to return a value:
myFirstPromise
.then(function resolveCB1(result) {
console.log(result);
result += ' again';
return result;
})
.then(function resolveCB2(result) {
console.log(result);
return result;
})
.catch(function rejectCB1(err) { console.log(err); });
Now that's said, you've got all the pieces together for understanding a Promise. Let's try to sum it up in a cleaner way:
var myFirstPromise = new Promise(function executor(resolve, reject) { resolve('resolved'); })
, resolveCB = function resolveCB(result) {
console.log(result);
result += ' again';
return result;
}
, resolveLastCB = function resolveLastCB(result) {
console.log(result);
result += ' and again';
return result;
}
, justLog = function justLog(result) {
console.log(result);
return result;
}
;
myFirstPromise
.then(resolveCB)
.then(resolveLastCB)
.then(justLog)
.catch(justLog);
You can now chan them nicely, it's cool and all
myFirstPromise
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveCB)
.then(resolveLastCB)
.then(justLog)
.catch(justLog);
But what if your Promise chain 'really' changes and you need to get rid of myFirstPromise and start with resolveCB instead ? It's just a function, it can be executed but doesn't have any .then() or .catch() method. It's not a Promise. You can't do resolveCB.then(resolveLastCB), it will thow an error resolveCB.then( is not a function or something similar. You might think this is a gross mistake, I didn't call resolveCB and resolveCB().then(resolveLastCB) should work? Unfortunalty for those who thought about that, it's still wrong. resolveCB returns a string, some characters, not a Promise.
In order to avoid this kind of maintenance issue, you should know that the resolve and reject callbacks can return a Promise instead of a value. To do so, we'll use what's called the factory pattern. In simple words, the factory pattern is about instanciating new object using a (static) function instead of using the constructor directly.
var myFirstPromiseFactory = function myFirstPromiseFactory() {
/*
return new Promise(function executor(resolve, reject) {
resolve('resolved');
});
if you just need to resolve a Promise, this is a quicker way
*/
return Promise.resolve('resolved');
}
, resolveFactory = function resolveFactory(result) {
return new Promise(function executor(resolve, reject) {
result = result || 'I started the chain';
console.log(result);
result += ' again';
return resolve(result); // you can avoid the return keyword if you want, I use it as a matter of readability
})
}
, resolveLastFactory = function resolveLastFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
result += ' and again';
return resolve(result);
});
}
, justLogFactory = function justLogFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
return resolve(result);
});
}
;
myFirstPromiseFactory() //!\\ notice I call the function so it returns a new Promise, previously we started directly with a Promise
.then(resolveFactory)
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
// Now you can switch easily, just call the first one
resolveFactory()
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
justLogFactory('I understand Javascript')
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
Factory functions might come handy when iterating over an array. It might be used to produce an array of promise given an input:
var myFirstPromiseFactory = function myFirstPromiseFactory() {
/*
return new Promise(function executor(resolve, reject) {
resolve('resolved');
});
if you just need to resolve a Promise, this is a quicker way
*/
return Promise.resolve('resolved');
}
, resolveFactory = function resolveFactory(result) {
return new Promise(function executor(resolve, reject) {
result = result || 'I started the chain';
console.log(result);
result += ' again';
return resolve(result); // you can avoid the return keyword if you want, I use it as a matter of readability
})
}
, resolveLastFactory = function resolveLastFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
result += ' and again';
return resolve(result);
});
}
, justLogFactory = function justLogFactory(result) {
return new Promise(function executor(resolve, reject) {
console.log(result);
return resolve(result);
});
}
, concatValues = function concatValues(values) {
return Promise.resolve(values.join(' '));
}
, someInputs = [
'I am an input'
, 'I am a second input'
, 'I am a third input'
, 'I am yet an other input'
]
;
myFirstPromiseFactory() //!\\ notice I call the function so it returns a new Promise, previously we started directly with a Promise
.then(resolveFactory)
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
// Now you can switch easily, just call the first one
resolveFactory()
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
justLogFactory('I understand Javascript')
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
// Using a factory functions to create an array of promise usable with Promise.all()
var promiseArray = someInputs.map(function(input) {
return justLogFactory(input);
});
Promise.all(promiseArray)
.then(concatValues)
.then(resolveLastFactory)
.then(justLogFactory)
.catch(justLogFactory);
I'm trying to return a value from a promise, but I can't. Why it doesn't work?
I get Promise { <pending> } output only.
function
function username() {
return new Promise(function (resolve, reject) {
user.where('id', req.id).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
variable
var r = username().then(function (a) {
return a.username;
});
console.log(r);
If I remove return and put console.log(a.username), it works, but it's not the result I want. I want to put returned value inside r.
EDIT #1
I need to pass my returned values into a view (like below), so I must be able to access them outside of the then() chain.
res.render("frontend/index", {
value1 : value1,
value2 : value2,
value3 : value3
});
EDIT #2
I'm using Express.js
Now I get "Error: Can't set headers after they are sent." error, I hope it's more clear now. When a user tries to access a page, I query the database and pass variables to a view, but there are more than one operation per view (username, post info, comments, etc).
exports.index = function (req, res) {
function username() {
return new Promise(function (resolve, reject) {
user.where('id', req.id).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
username().then(function (b) {
res.render('backend/index', {
username: b.username
});
});
function post() {
return new Promise(function (resolve, reject) {
post.where('id', req.params.postId).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
post().then(function (a) {
res.render('backend/index', {
post: a.post
});
});
};
The .then() function returns a promise only. Here in above code variable r is nothing but a promise reference object.
If you want to use the returned response from the promise, this is how you will do it -
username().then(function (a) {
console.log(JSON.stringify(a));
// you will need to use the response returned from server here..
// display in view or something else
res.render("frontend/index", {
value1 : a.value1,
value2 : a.value2,
value3 : a.value3
});
});
Returning a value from inside a promise thenable function will only return a promise and not the value.
You will need to wait till all the promises are resolved and then only send the response from the server. Once a response is sent, you can not send it again and hence the error headers....
Updated answer as per modified question -
exports.index = function (req, res) {
function username() {
return new Promise(function (resolve, reject) {
user.where('id', req.id).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
function post() {
return new Promise(function (resolve, reject) {
post.where('id', req.params.postId).fetch().then(function (data) {
data = data.toJSON();
resolve(data);
});
});
}
username().then(function (b) {
post().then(function (a) {
res.render('backend/index', {
post: a.post,
username: b.username
});
});
});
};
Try performing next process where fulfilled value of r is used inside of .then() to access asynchronous returned value from Promise r
var r = username().then(function (a) {
return a.username;
});
r.then(function(data) {
// `data` : `r` return value
// do stuff with `data` here
});
You'll have to restrict the usage of the value to functions passed to .then. There is no other logical way to assert that the value of r has been assigned.
var r;
username().then(function(a) {
r = a.username;
// use r here
// resolve promise with value of username
return r;
}).then(function(username) {
// or use it here
});