I'm trying to return promises from a promise and run Promise.all like this:
updateVideos()
.then(videos => {
return videos.map(video => updateUrl({ id: video, url: "http://..." }))
})
.then(Promise.all) // throw Promise.all called on non-object
How can I use this kind of Promise.all. I know .then(promises => Promise.all(promises)) works. But, just trying to know why that failed.
This happens with Express res.json too. The error message is different, but I think the reason is same.
For example:
promise().then(res.json) // Cannot read property 'app' of undefined
does not work but
promise().then(results =>res.json(results))
does.
all needs to be called with this referring to Promise (or a subclass), so you'd need:
.then(promises => Promise.all(promises))
or
.then(Promise.all.bind(Promise))
It's important because all needs to work correctly when inherited in Promise subclasses. For instance, if I do:
class MyPromise extends Promise {
}
...then the promise created by MyPromise.all should be created by MyPromise, not Promise. So all uses this. Example:
class MyPromise extends Promise {
constructor(...args) {
console.log("MyPromise constructor called");
super(...args);
}
}
console.log("Creating two generic promises");
const p1 = Promise.resolve("a");
const p2 = Promise.resolve("a");
console.log("Using MyPromise.all:");
const allp = MyPromise.all([p1, p2]);
console.log("Using then on the result:");
allp.then(results => {
console.log(results);
});
.as-console-wrapper {
max-height: 100% !important;
}
Details in the spec. (Which I'm going to have to re-read in order to understand why five calls to MyPromise are made when I call MyPromise.all in the above.)
TJ Crowder's response explained why this happens. But if you are looking for a different solution, BluebirdJS (an npm promise library) handles this situation a bit differently. The following code works fine for me.
Using bluebird can also be helpful for dealing with promises that need to be executed and evaluated sequentially. mapSeries has been a life-saver for me.
import * as Promise from "bluebird"
// ...
Promise.resolve()
.then(() => arrayOfPromises)
.then(Promise.all)
Related
Currently learning about promises, and I was trying to build a good connection and understanding of Promises in Asynchronous JavaScript. But I have become very curious about "Resolved", and "Reject" (I know that they are just variable name).
const promiseTest = new Promise((Resolved,Rejected) => {
Resolved("Success");
})
Well, I couldn't really figure out much while surfing through the internet on solutions, what I do know is that Resolved, and Rejected are passed in values, from where I am not sure, I assumed the Promise class at first, but it seems that is not it, and through research I found an article from freecodecamp mentioning that the JavaScript language sends it or so, a more detailed but simplified response would be appreciated it.
The promise constructor is going to make those functions, and then pass them to you. The functions are hidden from most of the world, but are exposed to you and only you, by passing them into your function
Here's some pseudocode of what it's doing:
class Promise {
constructor(yourFunction) {
const resolve = () => {
// some code which knows how to resolve the promise
}
const reject = () => {
// some code which knows how to reject the promise
}
yourFunction(resolve, reject);
}
}
I want to extend native Javascript Promise class with ES6 syntax, and be able to call some asynchronous function inside the subclass constructor. Based on async function result the promise must be either rejected or resolved.
However, two strange things happen when then function is called:
subclass constructor is executed twice
"Uncaught TypeError: Promise resolve or reject function is not callable" error is thrown
class MyPromise extends Promise {
constructor(name) {
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
}
new MyPromise('p1')
.then(result => {
console.log('resolved, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
The reasoning is simple but not necessarily self evident.
.then() returns a promise
if then is called on a subclass of Promise, the returned promise is an instance of the subclass, not Promise itself.
the then returned promise is constructed by calling the subclass constructor, and passing it an internal executor function that records the value of resolve and reject arguments passed to it for later use.
"later use" covers resolving or rejecting the promise returned by then asynchronously when monitoring execution of onfulfilled or onrejected handlers (later) to see if they return a value (which resolves the then returned promise) or throw an error (which rejects the promise).
In short then calls internally obtain and record references to the resolve and reject functions of promises they return.
So regarding the question,
new MyPromise( 'p1')
works fine and is the first call to the subclass constructor.
.then( someFunction)
records someFunction in a list of then calls made on new MyPromise (recall then can be called multiple times) and attempts to create a return promise by calling
new MyPromise( (resolve, reject) => ... /* store resolve reject references */
This is the second call to the subclass constructor coming from then code. The constructor is expected to (and does) return synchronously.
On return from creating the promise to return, the .then method makes an integrity check to see if the resolve and reject functions it needs for later use are in fact functions. They should have been stored (in a list) along with callbacks provided in the then call.
In the case of MyPromise they are not. The executor passed by then, to MyPromise, is not even called. So then method code throws a type error "Promise resolve or reject function is not callable" - it has no means of resolving or rejecting the promise it is supposed to return.
When creating a subclass of Promise, the subclass constructor must take an executor function as its first argument, and call the executor with real resolve and reject functional arguments. This is internally required by then method code.
Doing something intricate with MyPromise, perhaps checking the first parameter to see if it is a function and calling it as an executor if it is, may be feasible but is outside the scope of this answer! For the code shown, writing a factory/library function may be simpler:
function namedDelay(name, delay=1000, value=1) {
var promise = new Promise( (resolve,reject) => {
setTimeout(() => {
resolve(value)
}, delay)
}
);
promise.name = name;
return promise;
}
namedDelay( 'p1')
.then(result => {
console.log('fulfilled, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
;TLDR
The class extension to Promise is not an extension. If it were it would need to implement the Promise interface and take an executor function as first parameter. You could use a factory function to return a Promise which is resolved asynchronously (as above), or hack the posted code with
MyPromise.prototype.constructor = Promise
which causes .then to return a regular Promise object. The hack itself refutes the idea that a class extension is taking place.
Promise Extension Example
The following example shows a basic Promise extension that adds properties supplied to the constructor. Of note:
The Symbol.toString getter only affects the output of converting an instance to a string. It does not change "Promise" to "MyPromise" when logging an instance object on browser consoles tested.
Firefox 89 (Proton) is not reporting own properties of extended instances while Chrome does - the reason test code below logs instance properties by name.
class MyPromise extends Promise {
constructor(exec, props) {
if( typeof exec != "function") {
throw TypeError( "new MyPromise(executor, props): an executor function is required");
}
super((resolve, reject) => exec(resolve,reject));
if( props) {
Object.assign( this, props);
}
}
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
// Test the extension:
const p1 = new MyPromise( (resolve, reject) =>
resolve(42),
{id: "p1", bark: ()=>console.log("woof") });
console.log( "p1 is a %s object", p1.constructor.name);
console.log( "p1.toString() = %s", p1.toString());
console.log( "p1.id = '%s'", p1.id);
console.log( "p1 says:"); p1.bark();
const pThen = p1.then(data=>data);
console.log( "p1.then() returns a %s object", pThen.constructor.name);
let pAll = MyPromise.all([Promise.resolve(39)]);
console.log( "MyPromise.all returns a %s object", pAll.constructor.name);
try { new MyPromise(); }
catch(err) {
console.log( "new MyPromise() threw: '%s'", err.message);
}
The best way I found to extend a promise is
class MyPromise extends Promise {
constructor(name) {
// needed for MyPromise.race/all ecc
if(name instanceof Function){
return super(name)
}
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
// you can also use Symbol.species in order to
// return a Promise for then/catch/finally
static get [Symbol.species]() {
return Promise;
}
// Promise overrides his Symbol.toStringTag
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
new MyPromise('p1')
.then(result => {
console.log('resolved, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
The post by asdru contains the correct answer, but also contains an approach (constructor hack) that should be discouraged.
The constructor hack checks if the constructor argument is a function. This is not the way to go as the ECMAScript design contains a specific mechanism for sub-classing Promises via Symbol.species.
asdru's comment on using Symbol.species is correct. See the explanation in the current ECMAScript specification:
Promise prototype methods normally use their this value's constructor
to create a derived object. However, a subclass constructor may
over-ride that default behaviour by redefining its ##species property.
The specification (indirectly) refers to this note in the sections on finally and then (look for mentions of SpeciesConstructor).
By returning Promise as the species constructor, the problems that traktor's answer analyzes so clearly are avoided. then calls the Promise constructor, but not the sub-classed MyPromise constructor. The MyPromise constructor is called only once with the name argument and no further argument-checking logic is needed or appropriate.
Therefore, the code should simply be:
class MyPromise extends Promise {
constructor(name) {
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
static get [Symbol.species]() {
return Promise;
}
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
Less is more!
Some notes:
MDN has an example for the use of the species symbol in extending Array.
The most recent browser versions (Chrome, FF, Safari, Edge on MAC and Linux) handle this correctly, but I have no information on other browsers or legacy versions.
Symbol.toStringTag is a very nice touch, but not required. Most browsers use the value returned for this symbol to identify the sub-classed promise in the console, but, beware, FF does not - this could easily be confusing. In all browsers, however, new MyPromise('mine').toString() yields "[object MyPromise]".
All of this is also unproblematic if you author in Typescript.
As noseratio points out, a primary use-case for extending Promises is the wrapping of (legacy) APIs that support abort or cancel logic (FileReader, fetch, ...).
You have to make it thenable by implementing the then method.
Otherwise, that of the superclass, Promise, will be called, and it’ll try to create another Promise with your MyPromise’ constructor, which is not compatible with the original Promise constructor.
The thing is, it’s tricky to properly implement the then method that works just like Promise’s does. You’ll likely end up having an instance of Promise as a member, not as a superclass.
Summarizing the above into a short tl;dr for those who just want to get something working and move on, you just need to add this to your class body:
static get [Symbol.species]() {
return Promise;
}
A full TypeScript example:
class MyPromise extends Promise<string> {
constructor(name: string) {
super((resolve) => {
setTimeout(() => {
resolve(name)
}, 1000)
})
this.name = name;
}
static get [Symbol.species]() {
return Promise;
}
}
const p = new MyPromise('hi');
p.name === 'hi'
const x: string = await p;
x === 'hi'
That's all you need.
Forgoing Symbol.toStringTag may make MyPromise just look like a Promise in some debugging contexts, and you won't be able to do MyPromise.race or MyPromise.all, but that's fine: you can use Promise.all with a list of MyPromises.
This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 5 years ago.
I am writing a Node.js script to populate an SQL database with a test dataset.
In the promise chain illustrated in the code snippet below (the real code is a bit more hairy), the function insertData() requires the db object to be passed through from the previous stage. However, asynchronous calls inside dropAndCreateTables() on the previous stage use db object but do not return it. I came up with a solution that wraps promises inside dropAndCreateTables() into another promise object that resolves to a db object. However:
I heard that using Promise() constructor in non-library code is an antipattern and may lead to subtle and hard-to-diagnose mistakes
I heard that nesting then()-chains is also an antipattern
It does not allow me to ignore errors from the promiseDrop (for example, I don't care if tables don't exist on drop)
It is ugly
Questions:
Is there a simpler, nicer and more socially accepted way to override the return value of a promise? (in this case, created with Promise.all())
Is there a way to restructure my code in a way that this problem does not occur? (That is, I don't exclude the possibility of "XY problem" here)
Code:
const dropAndCreateTables = (db, startClean) => {
if(startClean) {
const sqlDrop = fs.readFileSync('drop.sql').toString()
const promiseDrop = db.raw(sqlDrop)
const sqlCreate = fs.readFileSync('create.sql').toString()
const promiseCreate = db.raw(sqlCreate)
/********* Problems here? ************************************/
return new Promise((resolve, reject) => { // Ew?
Promise.all([promiseDrop, promiseCreate])
.then(() => {
resolve(db) // Override the returned value
})
.catch(reject)
})
}
return Promise.resolve(db)
}
initDB({ debug: false })
.then((db) => {
return dropAndCreateTables(db, START_CLEAN) // Without my hack this does not return `db`
})
.then((db) => {
return insertData(db, DO_UPSERT) // This needs the `db` object
})
.then(() => {
console.info(`\n${timestamp()} done`)
})
.catch(handleError)
(Some fairly important notes midway and later in the answer, please do read all the way to the end.)
Is there a simpler, nicer and more socially accepted way to override the return value of a promise? (in this case, created with Promise.all())
Yes, you simply return a value from the then handler, and return the promise then returns:
return Promise.all([promiseDrop, promiseCreate])
.then(() => db);
then (and catch) create promise chains. Each link in the chain can transform the result. then and catch return a new promise that will be fulfilled or rejected based on what happens in their callback:
If their callback throws, the promise rejects with the error thrown
If their callback returns a non-thenable value (e.g., promise), the promise is fulfilled with that value
If their callback returns a thenable value, the promise is resolved to that thenable — it waits for the other promise to settle, then settles the same way
(If the term "thenable" isn't familiar, or you're not clear on the distinction between "fulfill" and "resolve," I go into promise terminology in this post on my blog.)
I heard that using Promise() constructor in non-library code is an antipattern and may lead to subtle and hard-to-diagnose mistakes
The distinction isn't library code vs. non-library code, it's between code that doesn't already have a promise to work with and code that does. If you already have a promise to work with, you almost never want to use new Promise. More: What is the explicit promise construction antipattern and how do I avoid it?
I heard that nesting then()-chains is also an antipattern
You almost never need to nest then chains, because again each link in the chain already has the means of tranforming the result passing through it. So:
// Unnecessary nesting
doSomething()
.then(a => {
return doSomethingElse(a * 2)
.then(b => b * 3);
})
.catch(e => { /*...handle error...*/ });
can be more idiomatically and simply written:
doSomething()
.then(a => doSomethingElse(a * 2))
.then(b => b * 3);
.catch(e => { /*...handle error...*/ });
Is there a way to restructure my code in a way that this problem does not occur? (That is, I don't exclude the possibility of "XY problem" here)
Not an X/Y per se, but you have a problem in that code: There's no guarantee the drop will happen before the create! So instead of starting both and letting them run in parallel and watching for the results with Promise.all, ensure those operations happen in sequence:
// Fairly minimal changes
const dropAndCreateTables = (db, startClean) => {
if(startClean) {
const sqlDrop = fs.readFileSync('drop.sql').toString()
return db.raw(sqlDrop)
.then(() => {
const sqlCreate = fs.readFileSync('create.sql').toString()
return db.raw(sqlCreate);
})
.then(() => db);
}
return Promise.resolve(db)
}
But, I wouldn't use sync file I/O. Instead
const promisify = require("utils").promisify;
const readWithPromise = promisify(fs.readFile);
and then
const dropAndCreateTables = (db, startClean) => {
if(startClean) {
const getDrop = readWithPromise('drop.sql'); // Start this first
const getCreate = readWithPromise('create.sql'); // Then start this
return getDrop
.then(dropSql => db.raw(dropSql)) // Got the drop SQL, run it
.then(() => getCreate) // Make sure we have the create SQl
.then(createSql => db.raw(createSql)) // Run it
.then(() => db);
}
return Promise.resolve(db)
}
Note how we avoid ever busy-waiting on I/O, and we can overlap the DB's drop operation with reading the create SQL.
You don't need to call the Promise constructor when returning another promise, you can just write it like:
return Promise.all([promiseDrop, promiseCreate])
.then(() => db)
.catch(error => {
// handle the error or rethrow it
})
You might omit resolving db from dropAndCreateTables like this:
.then((db) => {
return dropAndCreateTables(db, START_CLEAN).then(Promise.resolve(db));
})
You should not let dropAndCreateTables return a db promise, there is no real usecase for it. So:
return Promise.all([promiseDrop, promiseCreate]);
is enough. Now the chaining part:
initDB({ debug: false }).then(async (db) => {
await dropAndCreateTables(db, START_CLEAN);
await insertData(db, DO_UPSERT);
console.info(`\n${timestamp()} done`)
}).catch(handleError)
Background
I am trying to learn promises, and I have a promise chain I want to improve on.
Problem
While learning how to chain promises, I fail to see why anyone would rather return a promise instead of returning it's value.
Take the following example, which uses promise chaining:
let myObj = new MyClass();
myObj.getInfo()
.then(result => writeOutput(FILE_NAME, result))
.then(console.log(FILE_NAME + " complete"))
.catch(error => console.error(error));
class MyClass{
getInfo() {
return new Promise(function(fulfil, reject) {
fulfill("I like bananas");
});
}
Here I have to chain 2 times. But if I were to directly return the result from the method getInfo() instead of returning a Promise I could potentially do something like the following:
let myObj = new MyClass();
let str = myObj.getInfo();
writeOutput(FILE_NAME, str)
.then(console.log(FILE_NAME + " complete"))
.catch(error => console.error(error));
Questions
So as you can see I am a little confused.
Given that getInfo() is in fact async, is it possible to achieve a similar code to the one in my second code sample?
If it were possible, would it be a good idea? How would you do it?
You can only return a value from some function if that value is immediately available when that function is called (on the same tick of the event loop). Remember that return is synchronous.
If it is not available right away then you can only return a promise (or you can use a callback but here you are specifically asking about promises).
For a more detailed explanation see this answer that I wrote some time ago to a question asking on how to return a result of an AJAX call from some function. I explained why you cannot return the value but you can return a promise:
jQuery: Return data after ajax call success
Here is another related answer - it got downvoted for some reason but I think it explains a similar issue that you're asking about her:
Return value in function from a promise block
I fail to see why anyone would rather return a promise instead of returning it's value.
Because you don't have the value yet.
Given that getInfo() is in fact async, is it possible to achieve a similar code to the one in my second code sample?
If it's asynchronous, it must return a promise.
A syntax that avoids then calls (but still uses and produces promises) is made possible by async/await:
async function writeInfo(FILE_NAME) {
const myObj = new MyClass();
try {
const str = await myObj.getInfo();
await writeOutput(FILE_NAME, str);
console.log(FILE_NAME + " complete");
} catch (error) {
console.error(error);
}
}
writeInfo("…");
The purpose of a promise is to say: "The data will be there sometime, just continue with your coding for now", so that your page won't get stuck for example. Basically you are using Promises for example to load something from the server.
Another way of looking at the first question ("why would anyone..."):
When you call .then or .catch on a promise, the pending promise returned from the call is appended to the end of an existing promise chain. (Allowing the "existing chain" might only contain one promise).
When you resolve (not "fulfill", you can't fulfill a promise with a promise) a promise with a promise, the promise used in resolution is inserted at the head of what remains of the promise chain.
The promise used in resolution must be fulfilled with a value before the next .then or .catch clause in the chain is called.
I'm following the spec here and I'm not sure whether it allows onFulfilled to be called with multiple arguments. For example:
promise = new Promise(function(onFulfilled, onRejected){
onFulfilled('arg1', 'arg2');
})
such that my code:
promise.then(function(arg1, arg2){
// ....
});
would receive both arg1 and arg2?
I don't care about how any specific promises implementation does it, I wish to follow the w3c spec for promises closely.
I'm following the spec here and I'm not sure whether it allows onFulfilled to be called with multiple arguments.
Nope, just the first parameter will be treated as resolution value in the promise constructor. You can resolve with a composite value like an object or array.
I don't care about how any specific promises implementation does it, I wish to follow the w3c spec for promises closely.
That's where I believe you're wrong. The specification is designed to be minimal and is built for interoperating between promise libraries. The idea is to have a subset which DOM futures for example can reliably use and libraries can consume. Promise implementations do what you ask with .spread for a while now. For example:
Promise.try(function(){
return ["Hello","World","!"];
}).spread(function(a,b,c){
console.log(a,b+c); // "Hello World!";
});
With Bluebird. One solution if you want this functionality is to polyfill it.
if (!Promise.prototype.spread) {
Promise.prototype.spread = function (fn) {
return this.then(function (args) {
return Promise.all(args); // wait for all
}).then(function(args){
//this is always undefined in A+ complaint, but just in case
return fn.apply(this, args);
});
};
}
This lets you do:
Promise.resolve(null).then(function(){
return ["Hello","World","!"];
}).spread(function(a,b,c){
console.log(a,b+c);
});
With native promises at ease fiddle. Or use spread which is now (2018) commonplace in browsers:
Promise.resolve(["Hello","World","!"]).then(([a,b,c]) => {
console.log(a,b+c);
});
Or with await:
let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);
You can use E6 destructuring:
Object destructuring:
promise = new Promise(function(onFulfilled, onRejected){
onFulfilled({arg1: value1, arg2: value2});
})
promise.then(({arg1, arg2}) => {
// ....
});
Array destructuring:
promise = new Promise(function(onFulfilled, onRejected){
onFulfilled([value1, value2]);
})
promise.then(([arg1, arg2]) => {
// ....
});
The fulfillment value of a promise parallels the return value of a function and the rejection reason of a promise parallels the thrown exception of a function. Functions cannot return multiple values so promises must not have more than 1 fulfillment value.
As far as I can tell reading the ES6 Promise specification and the standard promise specification theres no clause preventing an implementation from handling this case - however its not implemented in the following libraries:
RSVP.promise (#L516-544)
Q promise (#787)
I assume the reason for them omiting multi arg resolves is to make changing order more succinct (i.e. as you can only return one value in a function it would make the control flow less intuitive) Example:
new Promise(function(resolve, reject) {
return resolve(5, 4);
})
.then(function(x,y) {
console.log(y);
return x; //we can only return 1 value here so the next then will only have 1 argument
})
.then(function(x,y) {
console.log(y);
});
De-structuring Assignment in ES6 would help here.For Ex:
let [arg1, arg2] = new Promise((resolve, reject) => {
resolve([argument1, argument2]);
});
Here is a CoffeeScript solution.
I was looking for the same solution and found seomething very intersting from this answer: Rejecting promises with multiple arguments (like $http) in AngularJS
the answer of this guy Florian
promise = deferred.promise
promise.success = (fn) ->
promise.then (data) ->
fn(data.payload, data.status, {additional: 42})
return promise
promise.error = (fn) ->
promise.then null, (err) ->
fn(err)
return promise
return promise
And to use it:
service.get().success (arg1, arg2, arg3) ->
# => arg1 is data.payload, arg2 is data.status, arg3 is the additional object
service.get().error (err) ->
# => err
Great question, and great answer by Benjamin, Kris, et al - many thanks!
I'm using this in a project and have created a module based on Benjamin Gruenwald's code. It's available on npmjs:
npm i -S promise-spread
Then in your code, do
require('promise-spread');
If you're using a library such as any-promise
var Promise = require('any-promise');
require('promise-spread')(Promise);
Maybe others find this useful, too!
Since functions in Javascript can be called with any number of arguments, and the document doesn't place any restriction on the onFulfilled() method's arguments besides the below clause, I think that you can pass multiple arguments to the onFulfilled() method as long as the promise's value is the first argument.
2.2.2.1 it must be called after promise is fulfilled, with promise’s value as its first argument.
To quote the article below, ""then" takes two arguments, a callback for a success case, and another for the failure case. Both are optional, so you can add a callback for the success or failure case only."
I usually look to this page for any basic promise questions, let me know if I am wrong
http://www.html5rocks.com/en/tutorials/es6/promises/