I'm trying to run 2 promises in paralel with sequelize, and then render the results in a .ejs template, but I'm receiving this error:
Promise.all(...).spread is not a function
This is my code:
var environment_hash = req.session.passport.user.environment_hash;
var Template = require('../models/index').Template;
var List = require('../models/index').List;
var values = {
where: { environment_hash: environment_hash,
is_deleted: 0
}
};
template = Template.findAll(values);
list = List.findAll(values);
Promise.all([template,list]).spread(function(templates,lists) {
res.render('campaign/create.ejs', {
templates: templates,
lists: lists
});
});
How can I solve thhis?
I'll make my comment into an answer since it solved your issue.
.spread() is not a standard promise method. It is available in the Bluebird promise library. Your code you included does not show that. Three possible solutions:
Access array values directly
You can just use .then(results => {...}) and access the results as results[0] and results[1].
Include the Bluebird Promise library
You can include the Bluebird promise library so you have access to .spread().
var Promise = require('bluebird');
Use destructuring in the callback arguments
In the latest versions of nodejs, you could also use destructuring assignment which kind of removes the need for .spread() like this:
Promise.all([template,list]).then(function([templates,lists]) {
res.render('campaign/create.ejs', {templates, lists});
});
You can write it without non-standard Bluebird features and keep less dependencies as well.
Promise.all([template,list])
.then(function([templates,lists]) {
};
ES6 Destructuring assignment
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
]).then(([one, two]) => console.log(one, two));
This is a Bluebird Promise feature and you can access it via Sequelize.Promise without installing Bluebird module itself
Sequelize.Promise.all(promises).spread(...)
I needed to install BlueBird.
npm install bluebird
Then:
var Promise = require("bluebird");
Related
I'm new to Nodejs and having trouble understand this issue: I tried to run a describe function against an array, and the AWS function seems to run after the main function has finished.
Here's the main function: (loop thru a list of ACM ARNs and check the status)
var checkCertStatus = function(resolveObj){
var promise = new Promise(function(resolve, reject){
console.log('1');
var retObj='';
resolveObj.Items.forEach(function(element) {
var certDescribeParams = {
CertificateArn: element.sslCertId
};
console.log('2');
acm.describeCertificate(certDescribeParams, function(err, data) {
if(err) reject(new Error(err));
else {
console.log(data.Certificate.DomainName + ': ' + data.Certificate.Status);
retObj+=data;
}
});
});
console.log('3');
resolve(retObj);
return promise;
})
}
Based on the debug log, assuming there are 2 items need to be processed, what I got:
1
2
2
3
example.com: ISSUED
example2.com: ISSUED
Basically, I need to pass this result to the next function in the chain (with promise and stuff).
Welcome to Node.js! Speaking generally, it might be helpful to study up on the asynchronous programming style. In particular, you seem to be mixing Promises and callbacks, which may make this example more confusing than it needs to be. I suggest using the AWS SDK's built-in feature to convert responses to Promises.
The first thing I notice is that you are manually constructing a Promise with a resolve/reject function. This is often a red flag unless you are creating a library. Most other libraries support Promises which you can simply use and chain. (This includes AWS SDK, as mentioned above.)
The second thing I notice is that your checkCertStatus function does not return anything. It creates a Promise but does not return it at the end. Your return promise; line is actually inside the callback function used to create the Promise.
Personally, when working with Promises, I prefer to use the Bluebird library. It provides more fully-featured Promises than native, including methods such as map. Conveniently, the AWS SDK can be configured to work with an alternative Promise constructor via AWS.config.setPromisesDependency() as documented here.
To simplify your logic, you might try something along these lines (untested code):
const Promise = require('bluebird');
AWS.config.setPromisesDependency(Promise);
const checkCertStatus = (resolveObj) => {
const items = resolveObj.Items;
console.log(`Mapping ${items.length} item(s)`);
return Promise.resolve(items)
.map((item) => {
const certDescribeParams = {
CertificateArn: item.sslCertId,
};
console.log(`Calling describeCertificate for ${item.sslCertId}`);
return acm.describeCertificate(certDescribeParams)
.promise()
.then((data) => {
console.log(`${data.Certificate.DomainName}: ${data.Certificate.Status}`);
return data;
});
});
};
We're defining checkCertStatus as a function which takes in resolveObj and returns a Promise chain starting from resolveObj.Items. (I apologize if you are not yet familiar with Arrow Functions.) The first and only step in this chain is to map the items array to a new array of Promises returned from the acm.describeCertificate method. If any one of these individual Promises fails, the top-level Promise chain will reject as well. Otherwise, the top-level Promise chain will resolve to an array of the results. (Note that I included an inessential .then step just to log the individual results, but you could remove that clause entirely.)
Hope this helps, and I apologize if I left any mistakes in the code.
When I start so code
import Promise from 'bluebird';
const mongodb = Promise.promisifyAll(require('mongodb'));
const MongoClient = mongodb.MongoClient;
MongoClient.connect(url).then((db) => {
return Promise.all([new WorkerService(db)]);
}).spread((workerService) => (
Promise.all([new WorkerRouter(workerService)])
)).spread((workerRouter) => {
app.use('/worker', workerRouter);
}).then(() => {
httpServer.start(config.get('server.port'));
}).catch((err) => {
console.log(err);
httpServer.finish();
});
I see so error
}).spread(function (workerService) {
^
TypeError: MongoClient.connect(...).then(...).spread is not a function
Please help me. What am I doing wrong?
I see several things wrong here:
The root cause here is that MongoClient.connect(...).then(...) is not returning a Bluebird promise, thus, there is no .spread() method. When you promisify an interface with Bluebird, it does NOT change the existing methods at all. Instead, it adds new methods with the "Async" suffix on it.
So, unless you've somehow told Mongo to create only Bluebird promises, then
`MongoClient.connect(...).then(...)`
will just be returning whatever kind of promise Mongo has built in. You will need to use the new promisified methods:
MongoClient.connectAsync(...).then(...).spread(...)
Some other issues I see in your code:
1) Promise.all() expects you to pass it an array of promises. But when you do this: Promise.all([new WorkerRouter(workerService)]), you're passing it an array of a single object which unless that object is a promise itself, is just wrong.
2) In this code:
}).spread((workerService) => (
Promise.all([new WorkerRouter(workerService)])
))
you need to return the resulting promise in order to link it into the chain:
}).spread((workerService) => (
return Promise.all([new WorkerRouter(workerService)])
))
3) But, there's no reason to use Promise.all() on a single element array. If it was a promise, then just return it.
4) It also looks like you're trying to use promises for different steps of synchronous code too. Besides just complicating the heck out of things, there's just no reason to do that as this:
}).spread((workerService) => (
Promise.all([new WorkerRouter(workerService)])
)).spread((workerRouter) => {
app.use('/worker', workerRouter);
}).then(() => {
httpServer.start(config.get('server.port'));
})...
can be combined to this:
)).spread((workerService) => {
app.use('/worker', new WorkerRouter(workerService));
httpServer.start(config.get('server.port'));
})...
And, can probably be condensed even further since new WorkerService(db) probably also doesn't return a promise.
5) And, then we can't really tell why you're even attempting to use .spread() in the first place. It is useful only when you have an array of results that you want to turn into multiple named arguments, but you don't need to have an array of results (since there was no reason to use Promise.all() with a single item and in modern version of node.js, there's really no reason to use .spread() any more because you can use ES6 destructing of an array function argument to accomplish the same thing with the regular .then().
I don't claim to follow exactly what you're trying to do with every line, but you may be able to do this:
import Promise from 'bluebird';
const mongodb = Promise.promisifyAll(require('mongodb'));
const MongoClient = mongodb.MongoClient;
MongoClient.connectAsync(url).then((db) => {
let service = new WorkerService(db);
app.use('/worker', new WorkerRouter(service));
// since httpServer.start() is async and there's no promise returning here,
// this promise chain will not wait for the server to start
httpServer.start(config.get('server.port'));
}).catch((err) => {
console.log(err);
httpServer.finish();
});
I am starting out with Node. Sorry for what probably is a stupid question.
Trying to understand why the below code throws an error: ReferenceError: Promise is not defined
allAccountFixtures: ['account-customer-joe', 'account-partner-sam', 'account-partner-jane', 'account-admin-jill'],
allProductFixtures: ['product-123', 'product-234', 'product-345', 'product-456'],
...
loadBasicFixtures: (Api) => {
return Promise.all([
Support.importRecords(Api.accountsAPI, Support.allAccountFixtures),
Support.importRecords(Api.productsAPI, Support.allProductFixtures)
]);
},
My APIs are defined elsewhere as:
this.accountsAPI = app.service('/api/accounts');
this.productsAPI = app.service('/api/products');
The import function is:
importRecords: (feathersService, fixtureNames) => {
// Wrap in an array if there's only one.
if (!(fixtureNames instanceof Array)) { fixtureNames = [fixtureNames]; }
// Create a separate promise for each JSON fixture to load the JSON from a
// file and send it to feathers.create(). Don't execute yet.
var promises = fixtureNames.map(fixtureName => {
var filePath = `test/fixtures/json/${fixtureName}.json`;
// console.log(`-> Loading JSON fixture: ${filePath}`);
return fs.readFileAsync(filePath, 'utf8')
.then((jsonString) => {
return JSON.parse(jsonString);
}).then((json) => {
return feathersService.create(json);
});
});
// Wrap all fixture loading promises inside a single outer promise that will
// fire when all of the child promises are complete.
return Promise.all(promises);
},
Don't know whether the supplied information is sufficient to advise what is happening. I looked up the concept of a "promise" and that's pretty much it. Perhaps you could point to the right direction. The documentation mentions resolve and reject.
I'll make my comment into an answer since it solved your issue.
Some older versions of node.js do not have promises built-in and to use promises with them requires loading a third party library that adds promise support.
If you upgrade to any 4.x version of node.js or newer, you will have promises built-in to node.js.
You need to import and require Promise
npm install promise --save
Then
var Promise = require('promise');
Using bluebird promises, I am trying to check if a certain version of a file exists (always only ONE of those files exists but i don't know which one). To speed things up, I am using the any() collection to get all checking out concurrently:
Promise.any([
fs.existsAsync('./foo/image1.jpg'),
fs.existsAsync('./foo/image2.gif'),
fs.existsAsync('./foo/image3.png')
]).then(function(result) {
console.log(result); //remains undefined
});
I am always getting an undefined result. According to (How do I return the response from an asynchronous call?) this is normal behaviour of a resolved promise. Still, I need to know, which of my three promises resolved or maybe I just can't use any() for that purpose?
The callback for fs.exists() does not follow the expected calling convention of callback(err, value) so it doesn't work with generic promisify. It uses just callback(value).
You could either use fs.statAsync() instead or you could create your own promisification for fs.exists() that would work properly.
Of course, fs.exists() is deprecated anyway for race condition reasons so you should perhaps rethink your tactic here anyway.
Here's a properly promisified fs.existsAsync():
fs.existsAsync = function(path) {
return new Promise(function(resolve) {
fs.exists(path, resolve);
});
}
You would assign this after you've done the general promisification of the fs module to replace the wrong fs.existsAsync that Bluebird did automatically.
I'm in the process of replacing some old code that used jQuery Deferred objects and I am rewriting using Bluebird/ES6 Promises.
If I have multiple asynchronous calls, how can I trigger a function after all the promises are resolved.
Using jQuery Deferreds it would be something like this:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
resuests.forEach(function(endpoint) {
promises.push($.ajax({url: endpoint}));
});
$.when.apply($, promises).then(function() {
alert('all promises complete!');
});
How do I rewrite this using ES6 Promise syntax?
Using Promise.all. Note that it takes an iterable such as an Array as its argument, unlike $.when, so doesn't need the .apply.
You'll also want to convert the jQuery Deferred to a native ES6 promise using Promise.resolve(thejQueryDeferred). EDIT: This gets done implicitly by the call to Promise.all, so is really optional.
Whole code:
var requests = [...]; //some arbitrary data that is iterated to generate multiple ajax requests
var promises = [];
requests.forEach(function(endpoint) {
var nativePromise = Promise.resolve($.ajax({url: endpoint})); // if you want to make it clear that you're converting from jQuery Deferred to ES6 promise!
promises.push(nativePromise);
});
Promise.all(promises).then(function() {
alert('all promises complete!');
});
Since this is tagged bluebird in addition to the two good solutions you have already gotten here is a more "bluebird" way:
var requests = [...];
Promise.map(requests, $.get).then(function(results){
alert('all promises complete!');
});
This is probably as simple as it gets.
As the others have pointed out, the native es6 way would be to use Promise.all, no Promise.resolve or explicit creation is needed. The cleanest way with native promises would probably be:
var requests = [...];
Promise.all(requests.map($.get)).then(function(results){
});