A standard promise with observable state and value
See original GitHub issueI’ve implemented a simple Promise
that has two observable properties: value
and state
. It is exactly like fromPromise
but it is actually a Promise
. As an example I have converted axios.get
result and:
const myPromise = toObservablePromise(axios.get('/api/get'));
// It is possible to write chained then functions
const myNewPromise = myPromise
.then(response => response.data)
.then(data => console.log(data))
// Also you can write a catch handler
.catch(err => console.error(err));
// And in every step it is possible to observe the state
when(
() => myNewPromise.state === 'fulfilled',
() => {
console.log(myNewPromise.value); // data
});
when(
() => myPromise.state === 'fulfilled',
() => {
console.log(myPromise.value); // response
});
// It is possible to write async tests easily
it('should run this async test without done function', () => {
return myNewPromise
.then(data => {
expect(data).toEqual(...);
});
});
// Also it is possible to watch the whole process using autorun
autorun(() => {
console.log('Promise state=', myNewPromise.state, ', value=', myNewPromise.value);
});
// Promise state=pending, value=undefined
// Promise state=fulfilled, value=<data>
// also you can use Promise.all
Promise.all(myPromise, new ObservablePromise(Promise.resolve('value')), aNativePromise);
Having this gives the feeling that you are working with a native promise 😆 .
Here is a simple implementation for ObservablePromise
and toObservablePromise
(Promise implementation pasted from here):
import { observable } from 'mobx';
/**
* Check if a value is a Promise and, if it is,
* return the `then` method of that promise.
*
* @param {Promise|Any} value
* @return {Function|Null}
*/
function getThen(value) {
const t = typeof value;
if (value && (t === 'object' || t === 'function')) {
const then = value.then;
if (typeof then === 'function') {
return then;
}
}
return null;
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*
* @param {Function} fn A resolver function that may not be trusted
* @param {Function} onFulfilled
* @param {Function} onRejected
*/
function doResolve(fn, onFulfilled, onRejected) {
let done = false;
try {
fn((value) => {
if (done) return;
done = true;
onFulfilled(value);
}, (reason) => {
if (done) return;
done = true;
onRejected(reason);
});
} catch (ex) {
if (done) return;
done = true;
onRejected(ex);
}
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function ObservablePromise(fn) {
// store state which can be PENDING, FULFILLED or REJECTED
const state = observable.box(PENDING);
// store value once FULFILLED or REJECTED
const value = observable.box(null);
// store sucess & failure handlers
let handlers = [];
function handle(handler) {
if (state.get() === PENDING) {
handlers.push(handler);
} else {
if (state.get() === FULFILLED &&
typeof handler.onFulfilled === 'function') {
handler.onFulfilled(value.get());
}
if (state.get() === REJECTED &&
typeof handler.onRejected === 'function') {
handler.onRejected(value.get());
}
}
}
function fulfill(result) {
value.set(result);
state.set(FULFILLED);
handlers.forEach(handle);
handlers = null;
}
function reject(error) {
value.set(error);
state.set(REJECTED);
handlers.forEach(handle);
handlers = null;
}
function resolve(result) {
try {
const then = getThen(result);
if (then) {
doResolve(then.bind(result), resolve, reject);
return;
}
fulfill(result);
} catch (e) {
reject(e);
}
}
this.done = (onFulfilled, onRejected) => {
// ensure we are always asynchronous
setTimeout(() => {
handle({
onFulfilled,
onRejected
});
}, 0);
};
this.then = (onFulfilled, onRejected) =>
new ObservablePromise((resolveFn, rejectFn) => this.done((result) => {
if (typeof onFulfilled === 'function') {
try {
return resolveFn(onFulfilled(result));
} catch (ex) {
return rejectFn(ex);
}
} else {
return resolveFn(result);
}
}, (error) => {
if (typeof onRejected === 'function') {
try {
return resolveFn(onRejected(error));
} catch (ex) {
return rejectFn(ex);
}
} else {
return rejectFn(error);
}
}));
this.catch = (onRejected) => this.then(null, onRejected);
Object.defineProperty(this, 'value', {
get() {
return value.get();
}
});
Object.defineProperty(this, 'state', {
get() {
return state.get();
}
});
doResolve(fn, resolve, reject);
}
export default function toObservablePromise(promise) {
return new ObservablePromise((resolve, reject) => {
promise.then(resolve, reject);
});
}
export { ObservablePromise };
Issue Analytics
- State:
- Created 7 years ago
- Comments:9 (4 by maintainers)
Top Results From Across the Web
What is the difference between Promises and Observables?
Promises and Observables both handle the asynchronous call only. A promise does not emit a value at all - a promise is a...
Read more >Promise - JavaScript - MDN Web Docs
A "fulfilled" state indicates a successful completion of the promise, while a "rejected" state indicates a lack of success. The return value of ......
Read more >JavaScript Promises vs. RxJS Observables - Auth0
A JavaScript Promise is an object that produces a single value, asynchronously. Data goes in, and a single value is emitted and used....
Read more >RxJS Tips — Promises Into Observables - Medium
Using the merge() RxJS operator we are able to create an Observable that uses the values from all 3 sources and will fire...
Read more >Observable methods to Promises - Nuclia
infer V is a type variable that will be replaced by the type of the value returned by the observable returned by the...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
If we could chain
ObservablePromise
this would be the implementation ofLoading
component (without throwing a promise):My argument is that if we could hack around promise, we could do some cool things like this example. Currently, it is not possible because
then
andcatch
methods ofObservablePromise
does not return anObservablePromise
.Regarding the first example, Mobx has been already hacking/wrapping native apis to make them still feel as native apis with additional capabilities as being observed. The same can be applied here, for the promise, to leave the touch and feel of the native api with additional capabilities.
Thanks for the idea @alisabzevari.
fromPromise
now returns a promise in 3.0.0, which is enhanced with the typicalfromPromise
capabilities