I've just been playing with Promises and like them a lot. But one thing I find strange is that ".then()" creates a new promise, but with no way to reject it.
ie. I can't write:
return new Promise((resolve, reject) => {
// Do some stuff, call resolve()/reject() on success/failure.
}).then((step1Val, resolve, reject) => {
// Do some stuff, call resolve()/reject() on success/failure.
// But this doesn't actually work, because the "then" callback
// doesn't get resolve/reject as args.
});
Instead I'm having to write this as the following, which is more verbose:
return new Promise((resolve, reject) => {
// Do some stuff, call resolve()/reject() on success/failure.
}).then(new Promise((resolve, reject) = {
// Do some stuff, call resolve()/reject() on success/failure.
// But this way I don't get access to step1Val.
}));
Another bummer of this style is that that the second step doesn't get access to the first step's value.
Promise.resolve() and Promise.reject() return a promise resolved or rejected to the value passed in the first argument. Returning a promise in the fulfillment function passed to .then() chains the promises together.
somethingThatReturnsPromise()
.then((foo) => {
return foo.bar;
// Or if you like it more verbose
return Promise.resolve(foo.bar);
// Or pass bar to a function modifying bar that returns a promise
return modifyBarReturnPromise(foo.bar);
})
.then((newBar) => {
console.log(newBar);
});
In your case, if you need step1Val in next promise chain, I personally do this, however people more familiar with promises may know of a better way to do it (maybe with something like Promise.all() or Promise.props() in the BlueBird library).
It took me a while to understand this correct response because I couldn't understand the documentation for promises. For the benefit of any other person who was likewise confused...
then() returns a new promise resolved to the return value of the function. However, if that value is itself a promise, then it follows the promise chain and passes the eventual state to the next then()/catch() call.
You don't need resolve or reject. Just return the promise:
.then(val => doSomeStuff())
Thats it. The resulting promise will get resolved/rejected based on the return result of doSomeStuff()
You can also use throw to reject a promise manually, or return Promise.reject()
.then(val => {
if (something) return successVal;
else throw new Error("Failure");
});
For more complex scenarios where I need the values from previous actions, I like the forgoing the chaining and using a join helper to unwrap any set of promises as needed:
let url = getUrl(resource);
let data = url.then(url => fetch(url))
let updateData = d => _.assign(d, {field: 'newVal'}
let update = join(url, data, (url, data) => sendUpdate(url, updateData(data))
I like Jquery's Deferred better then ES6 Promise. Promises lack the `always` callback, they don't have any `progress` events.
The spec on mdn[0] doesn't mention asynchronous `then` or `catch` behavior. If the callback in Jquery `Deferred#then` returns a Deferred that deferred will be returned by `then`.
// Basic async function, resolves after n milliseconds
function wait(n) {
var promise = $.Deferred();
setTimeout(promise.resolve, n);
return promise;
}
wait(10)
.then(function() {
console.log('first'); // prints first after 10 milliseconds
return wait(10);
})
.then(function() {
console.log('second'); // prints second after 20 milliseconds
return 'done';
})
You can 'flip' a failed promise by returning a resolve promise in the fail callback.
var promise = $.Deferred();
setTimeout(promise.reject, 100)
promise.then(null, function () {
return $.Deferred().resolve([]);
}).done(function(arg) {
console.log(arg); // Prints '[]'
})
Not quite :) See my paste above, it returns a new Promise in pending state that will return the value in the new then block ("5" here), possibly immediately. (i.e. pending for one event loop tick, then resolved)
In this example, I've sequenced the resolution of two promises (async functions return Promises when invoked)
If either of these Promises reject or throw an error, or if any of the code within the try {} block throws an error, the error will be caught inline in the catch block (one area only).
You can also see that both Promise's return values are in-scope and continually usable (vs. losing scope with 'then' chains).
I see. Not sure what the best practice is for the losing scope in then chains issue with promises. I ended up creating something along the lines of a message object designed for each chain, which felt like a smell.
IMO threading an object through is probably the best method, unfortunately. You could use an Immutable Record or something to help keep it under control.
I would personally just use async/await to more explicitly handle the sequencing/binding.
Because the initial one is where you'd interface (potentially) with non-promise code. E.g. in order to wrap a node-style function, you can't throw or return. But in general you shouldn't need to use `new Promise()`, that should in most cases be reserved to more general, low-level code (e.g. a promisify implementation).
I'm writing code against indexedDB (which doesn't use promises), but I want to expose promises to my callers. So I'm wrapping my indexedDB usage in Promises.
Also some of indexedDB doesn't seem like it would fit with promises, since some operations have 3 or more callbacks (onsuccess, onerror, onupgradeneeded).
There are a number of IndexedDb libraries that uses promises. However, there is or was a problem with how IndexedDb is specified to work that is not really compatible with promises. I'm not sure if they have fixed this yet.
https://github.com/promises-aplus/promises-spec/issues/45#is...
I can see the benefits of the indexedDb behavior: an open transaction is an exclusive resource, so leaving one dangling locks out other transactions. The auto-commit behavior means it's a lot harder to accidentally leave a transaction dangling. But it is unfortunate that this makes it not play nicely with promises.
It makes explicit that the intention that a promise should be rejected, as opposed to a throw where one might reasonably expect an exception of some sort.
ie. I can't write:
Instead I'm having to write this as the following, which is more verbose: Another bummer of this style is that that the second step doesn't get access to the first step's value.