Simple Promise and Then implementation
Here's the shortened code for creating a promise class,
class MyPromise { constructor(executor) { this.callbacks = [];
const resolve = res => { for (const { callback } of this.callbacks) { callback(res); } };
executor(resolve); }
then(callback) { return new MyPromise((resolve) => { const done = res => { resolve(callback(res)); }; this.callbacks.push({ callback: done }); }); }}
promise = new MyPromise((resolve) => { setTimeout(() => resolve(2), 1000);});
promise.then(result => { console.log(result); return 2 * result;}).then(result => console.log(result));
How to implement a simpler Promise in JavaScript?
With the comments that you guys provide me, I was able to improve the code and correct the errors mentioned, as shown below. Now, I would like you to give me suggestions on how to proceed and improve the code. Thanks. (The code can also be found on github).
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function _Promise(executor) {
let state = PENDING;
let callOnFulfilled = [];
let callOnRejected = undefined;;
function resolve(...args) {
if (!state) {
state = FULFILLED;
}
resolveCallbacks(...args);
};
function reject(error) {
state = REJECTED;
if (callOnRejected && (callOnRejected instanceof Function)) {
callOnRejected(error);
callOnRejected = undefined;
callOnFulfilled = [];
} else {
throw `Unhandled Promise Rejection\n\tError: ${error}`;
}
};
function resolveCallbacks(...value) {
if (state !== REJECTED) {
let callback = undefined;
do {
callback = callOnFulfilled.shift();
if (callback && (callback instanceof Function)) {
const result = callback(...value);
if (result instanceof _Promise) {
result.then(resolveCallbacks, reject);
return;
} else {
value = [result];
}
}
} while (callback);
}
};
if (executor && (executor instanceof Function)) {
executor(resolve, reject);
}
this.then = function (onFulfilled, onRejected) {
if (onFulfilled) {
callOnFulfilled.push(onFulfilled);
if (state === FULFILLED) {
resolveCallbacks();
}
}
if (onRejected && !callOnRejected) {
callOnRejected = onRejected;
}
return this;
};
this.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
}
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
return 25;
});
}).then(function (value) {
console.log(value);
console.timeEnd('codeExecution');
});
}).catch(function (error) {
console.log(error);
});
How JavaScript promises work behind the scenes
The following is a simplified implementation of the built-in Promise class. catch
and finally
have not been implemented.
The function supplied to the Promise constructor is called the executor function, and is invoked immediately and synchronously.
Every promise has a method .then
, enabling the chaining of promises.
Functions supplied to .then
are always invoked asynchronously on a microtask (note use of queueMicrotask
below).
Every time .then
is called, a new promise is created and returned.
.then
can be called more than once on the same promise, creating a multicast of the result of the promise, and a branching of the promise chain.
A promise can be in one of three states: pending, fulfilled, or rejected. State transitions are unidirectional: you cannot move from fulfilled or rejected, back to pending.
If a promise is resolved with another promise, then the two promise chains are joined and the outer promise takes on the status of the inner promise (which could be pending), until the inner promise resolves.
function Promise(executor) { if (!executor) throw "Promise executor undefined" let status = "pending", value, thenQ = []
const then = onFulfilled => { let resolver // This ensures control does not move to later promises // until prior promises have been resolved. const nextPromise = new Promise(resolve => (resolver = resolve)) // More than one "then" can be registered with each promise. thenQ.push((...args) => resolver(onFulfilled(...args))) return nextPromise }
// We check if the result is a "thenable"; if so, we treat // it as an inner promise, otherwise we simply fulfil with // the result. const resolve = result => result?.then ? result.then(fulfil) : fulfil(result)
// When a promise has been fulfilled, its "thens" can be run. const fulfil = result => (status = "fulfilled", value = result, executeThens(value))
// "Thens" are run asynchronously, on a microtask. const executeThens = value => queueMicrotask(() => thenQ.forEach(el => el(value)))
// The executor is run synchronously. executor(resolve)
return { then, get status() { return status }, get value() { return value } }}
// Chainingnew Promise(resolve => { console.log('Waiting for step 1...') setTimeout(() => resolve("One, two..."), 1500)}).then(result => new Promise(resolve => { console.log('Waiting for step 2...') setTimeout(() => resolve(`${result}three, four`), 1500)})).then(result => console.log(`Chaining result: ${result}.`))
// Branchingconst p = new Promise(resolve => { console.log('Waiting for step a...') setTimeout(() => resolve("Alpha, Bravo..."), 1500)})
p.then(result => new Promise(resolve => { console.log('Waiting for step b1...') setTimeout(() => resolve(`${result}Charlie, Delta`), 1500)})).then(console.log)
p.then(result => { console.log('Waiting for step b2...') return `${result}Echo, Foxtrot`}).then(console.log)
Understanding setTimeout and its implementation in JavaScript Promises
This is not a correct Promise implementation. It clearly has no capabilities for rejections, but also for the implemented fulfilment feature it does not comply with the Promises/A+ specification. Just to give two examples:
It does not comply with rule 2.1.2.2
When fulfilled, a promise must have a value, which must not change.
....nor with rule 2.2.2.3:
If
onFulfilled
is a function it must not be called more than once.In your implementation, if you would add a second call to
resolve
:new MyPromise(function(resolve, reject) {
resolve('new message'); resolve('new message2');
}).then((function(message) {
console.log(message);
// ... etc
})...then both calls to
resolve
would fire thethen
callback, and show that the promised value was modified after the first time it was set. This is in complete violation of the principle of promises: promises can only resolve once.It does not comply with rule 2.2.6:
then
may be called multiple times on the same promise.- If/when
promise
is fulfilled, all respectiveonFulfilled
callbacks must execute in the order of their originating calls tothen
.
This does not happen if we use the following code:
let p = new MyPromise(resolve => resolve("value"));
p.then(console.log); // This callback is called
p.then(console.log); // This callback is not called -> violation!- If/when
These are basic shortcomings, and this is just the tip of the iceberg.
If you are interested in how it could be implemented in compliance with Promise/A+, then have a look at one I did a few years ago with a step-by-step explanation.
As to your question
how does the program even get to the
setTimeout
?
When your main code executes this:
new MyPromise(function(resolve, reject) {
resolve('new message');
})
...then the parameter variable configFunction
is initialised with that callback function. The MyPromise
constructor calls it, passing it the following callback as first argument:
function(message){
setTimeout(function(){
if(nextSuccessCallBack) {
var result = nextSuccessCallBack(message);
if(result && result.then) {
result.then(nextResolve);
} else {
nextResolve && nextResolve(result);
}
}
})
}
So that means that resolve
in your main code's callback function is initialised with the above callback function. Then your main code's callback function calls resolve
with a string argument. Since resolve
is the above callback function, we can see it gets executed with message
initialised to "new message". This function executes setTimeout
.
Trying to implement a SIMPLE promise in Reactjs
You are using React
in a wrong way. A Promise
is designed to return result at a later point of time. By the time your promise has been resolved
or rejected
, your render
would have finished execution and it wont update when the promise completes.
render
method should only depend on props
and/or state
to render the desired output. Any change to prop
or state
would re-render
your component.
- First, identify where your
Promise
should go in the life cycle of the component(here) In your case i would do the following
Initialize an state inside your constructor(ES6) or via
getInitialState
constructor(props) {
super(props);
this.state = {
name: '',
};
}Then on
componentWillMount
orcomponentDidMount
which ever suits you, call the promise therecomponentWillMount() {
var promise = new Promise( (resolve, reject) => {
let name = 'Paul'
if (name === 'Paul') {
resolve("Promise resolved successfully");
}
else {
reject(Error("Promise rejected"));
}
});
let obj = {newName: ''};
promise.then( result => {
this.setState({name: result});
}, function(error) {
this.setState({name: error});
});
}Then in
render
method write something similar to this.render() {
return (
<div className="App">
<h1>Hello World</h1>
<h2>{this.state.name}</h2>
</div>
);
}
Related Topics
Escaping Strings in JavaScript
How to Use React Hooks in React Classic 'Class' Component
Remove All Event Listeners of Specific Type
How to Transpose a JavaScript Object into a Key/Value Array
How to Set Locale in Datepipe in Angular 2
What Is Ajax and How Does It Work
Differencebetween JavaScript Promises and Async Await
Js:Convert Array of Strings to Array of Objects
How to Extend Function with Es6 Classes
"Object Doesn't Support This Property or Method" Ie10/11
Adding Console.Log to Every Function Automatically
How to Do Associative Array/Hashing in JavaScript
Using JavaScript to Display a Blob
What Is the Motivation for Bringing Symbols to Es6
Multiple Inheritance/Prototypes in JavaScript