Think of how Rails, e.g. allows you to define a property as associated with another:
class Customer < ActiveRecord::Base
has_many :orders
end
This does not set up a database column for orders. Instead, it creates a getter for orders, which allows us to do
#orders = #customer.orders
Which goes and gets the related orders objects.
In JS, we can easily do that with getters:
{
name: "John",
get orders() {
// get the order stuff here
}
}
But Rails is sync, and in JS, if in our example, as is reasonable, we are going to the database, we would be doing it async.
How would we create async getters (and setters, for that matter)?
Would we return a promise that eventually gets resolved?
{
name: "John",
get orders() {
// create a promise
// pseudo-code for db and promise...
db.find("orders",{customer:"John"},function(err,data) {
promise.resolve(data);
});
return promise;
}
}
which would allow us to do
customer.orders.then(....);
Or would we do it more angular-style, where we would automatically resolve it into a value?
To sum, how do we implement async getters?
The get and set function keywords seem to be incompatible with the async keyword. However, since async/await is just a wrapper around Promises, you can just use a Promise to make your functions "await-able".
Note: It should be possible to use the Object.defineProperty method to assign an async function to a setter or getter.
getter
Promises work well with getters.
Here, I'm using the Node.js 8 builtin util.promisify() function that converts a node style callback ("nodeback") to a Promise in a single line. This makes it very easy to write an await-able getter.
var util = require('util');
class Foo {
get orders() {
return util.promisify(db.find)("orders", {customer: this.name});
}
};
// We can't use await outside of an async function
(async function() {
var bar = new Foo();
bar.name = 'John'; // Since getters cannot take arguments
console.log(await bar.orders);
})();
setter
For setters, it gets a little weird.
You can of course pass a Promise to a setter as an argument and do whatever inside, whether you wait for the Promise to be fulfilled or not.
However, I imagine a more useful use-case (the one that brought me here!) would be to use to the setter and then awaiting that operation to be completed in whatever context the setter was used from. This unfortunately is not possible as the return value from the setter function is discarded.
function makePromise(delay, val) {
return new Promise(resolve => {
setTimeout(() => resolve(val), delay);
});
}
class SetTest {
set foo(p) {
return p.then(function(val) {
// Do something with val that takes time
return makePromise(2000, val);
}).then(console.log);
}
};
var bar = new SetTest();
var promisedValue = makePromise(1000, 'Foo');
(async function() {
await (bar.foo = promisedValue);
console.log('Done!');
})();
In this example, the Done! is printed to the console after 1 second and the Foo is printed 2 seconds after that. This is because the await is waiting for promisedValue to be fulfilled and it never sees the Promise used/generated inside the setter.
As for asynchronous getters, you may just do something like this:
const object = {};
Object.defineProperty(object, 'myProperty', {
async get() {
// Your awaited calls
return /* Your value */;
}
});
Rather, the problem arises when it comes to asynchronous setters.
Since the expression a = b always produce b, there is nothing one can do to avoid this, i.e. no setter in the object holding the property a can override this behavior.
Since I stumbled upon this problem as well, I could figure out asynchronous setters were literally impossible. So, I realized I had to choose an alternative design for use in place of async setters. And then I came up with the following alternative syntax:
console.log(await myObject.myProperty); // Get the value of the property asynchronously
await myObject.myProperty(newValue); // Set the value of the property asynchronously
I got it working with the following code,
function asyncProperty(descriptor) {
const newDescriptor = Object.assign({}, descriptor);
delete newDescriptor.set;
let promise;
function addListener(key) {
return callback => (promise || (promise = descriptor.get()))[key](callback);
}
newDescriptor.get = () => new Proxy(descriptor.set, {
has(target, key) {
return Reflect.has(target, key) || key === 'then' || key === 'catch';
},
get(target, key) {
if (key === 'then' || key === 'catch')
return addListener(key);
return Reflect.get(target, key);
}
});
return newDescriptor;
}
which returns a descriptor for an asynchronous property, given another descriptor that is allowed to define something that looks like an asynchronous setter.
You can use the above code as follows:
function time(millis) {
return new Promise(resolve => setTimeout(resolve, millis));
}
const object = Object.create({}, {
myProperty: asyncProperty({
async get() {
await time(1000);
return 'My value';
},
async set(value) {
await time(5000);
console.log('new value is', value);
}
})
});
Once you've set up with an asynchronous property like the above, you can set it as already illustrated:
(async function() {
console.log('getting...');
console.log('value from getter is', await object.myProperty);
console.log('setting...');
await object.myProperty('My new value');
console.log('done');
})();
The following allows for async setters in proxy handlers following the convention in Davide Cannizzo's answer.
var obj = new Proxy({}, asyncHandler({
async get (target, key, receiver) {
await new Promise(a => setTimeout(a, 1000))
return target[key]
},
async set (target, key, val, receiver) {
await new Promise(a => setTimeout(a, 1000))
return target[key] = val
}
}))
await obj.foo('bar') // set obj.foo = 'bar' asynchronously
console.log(await obj.foo) // 'bar'
function asyncHandler (h={}) {
const getter = h.get
const setter = h.set
let handler = Object.assign({}, h)
handler.set = () => false
handler.get = (...args) => {
let promise
return new Proxy(()=>{}, {
apply: (target, self, argv) => {
return setter(args[0], args[1], argv[0], args[2])
},
get: (target, key, receiver) => {
if (key == 'then' || key == 'catch') {
return callback => {
if (!promise) promise = getter(...args)
return promise[key](callback)
}
}
}
})
}
return handler
}
Here's another approach to this. It creates an extra wrapper, but in other aspects it covers what one would expect, including usage of await (this is TypeScript, just strip the : Promise<..> bit that sets return value type to get JS):
// this doesn't work
private get async authedClientPromise(): Promise<nanoClient.ServerScope> {
await this.makeSureAuthorized()
return this.client
}
// but this does
private get authedClientPromise(): Promise<nanoClient.ServerScope> {
return (async () => {
await this.makeSureAuthorized()
return this.client
})()
}
Here's how you could implement your get orders function
function get(name) {
return new Promise(function(resolve, reject) {
db.find("orders", {customer: name}, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}
You could call this function like
customer.get("John").then(data => {
// Process data here...
}).catch(err => {
// Process error here...
});
Related
Think of how Rails, e.g. allows you to define a property as associated with another:
class Customer < ActiveRecord::Base
has_many :orders
end
This does not set up a database column for orders. Instead, it creates a getter for orders, which allows us to do
#orders = #customer.orders
Which goes and gets the related orders objects.
In JS, we can easily do that with getters:
{
name: "John",
get orders() {
// get the order stuff here
}
}
But Rails is sync, and in JS, if in our example, as is reasonable, we are going to the database, we would be doing it async.
How would we create async getters (and setters, for that matter)?
Would we return a promise that eventually gets resolved?
{
name: "John",
get orders() {
// create a promise
// pseudo-code for db and promise...
db.find("orders",{customer:"John"},function(err,data) {
promise.resolve(data);
});
return promise;
}
}
which would allow us to do
customer.orders.then(....);
Or would we do it more angular-style, where we would automatically resolve it into a value?
To sum, how do we implement async getters?
The get and set function keywords seem to be incompatible with the async keyword. However, since async/await is just a wrapper around Promises, you can just use a Promise to make your functions "await-able".
Note: It should be possible to use the Object.defineProperty method to assign an async function to a setter or getter.
getter
Promises work well with getters.
Here, I'm using the Node.js 8 builtin util.promisify() function that converts a node style callback ("nodeback") to a Promise in a single line. This makes it very easy to write an await-able getter.
var util = require('util');
class Foo {
get orders() {
return util.promisify(db.find)("orders", {customer: this.name});
}
};
// We can't use await outside of an async function
(async function() {
var bar = new Foo();
bar.name = 'John'; // Since getters cannot take arguments
console.log(await bar.orders);
})();
setter
For setters, it gets a little weird.
You can of course pass a Promise to a setter as an argument and do whatever inside, whether you wait for the Promise to be fulfilled or not.
However, I imagine a more useful use-case (the one that brought me here!) would be to use to the setter and then awaiting that operation to be completed in whatever context the setter was used from. This unfortunately is not possible as the return value from the setter function is discarded.
function makePromise(delay, val) {
return new Promise(resolve => {
setTimeout(() => resolve(val), delay);
});
}
class SetTest {
set foo(p) {
return p.then(function(val) {
// Do something with val that takes time
return makePromise(2000, val);
}).then(console.log);
}
};
var bar = new SetTest();
var promisedValue = makePromise(1000, 'Foo');
(async function() {
await (bar.foo = promisedValue);
console.log('Done!');
})();
In this example, the Done! is printed to the console after 1 second and the Foo is printed 2 seconds after that. This is because the await is waiting for promisedValue to be fulfilled and it never sees the Promise used/generated inside the setter.
As for asynchronous getters, you may just do something like this:
const object = {};
Object.defineProperty(object, 'myProperty', {
async get() {
// Your awaited calls
return /* Your value */;
}
});
Rather, the problem arises when it comes to asynchronous setters.
Since the expression a = b always produce b, there is nothing one can do to avoid this, i.e. no setter in the object holding the property a can override this behavior.
Since I stumbled upon this problem as well, I could figure out asynchronous setters were literally impossible. So, I realized I had to choose an alternative design for use in place of async setters. And then I came up with the following alternative syntax:
console.log(await myObject.myProperty); // Get the value of the property asynchronously
await myObject.myProperty(newValue); // Set the value of the property asynchronously
I got it working with the following code,
function asyncProperty(descriptor) {
const newDescriptor = Object.assign({}, descriptor);
delete newDescriptor.set;
let promise;
function addListener(key) {
return callback => (promise || (promise = descriptor.get()))[key](callback);
}
newDescriptor.get = () => new Proxy(descriptor.set, {
has(target, key) {
return Reflect.has(target, key) || key === 'then' || key === 'catch';
},
get(target, key) {
if (key === 'then' || key === 'catch')
return addListener(key);
return Reflect.get(target, key);
}
});
return newDescriptor;
}
which returns a descriptor for an asynchronous property, given another descriptor that is allowed to define something that looks like an asynchronous setter.
You can use the above code as follows:
function time(millis) {
return new Promise(resolve => setTimeout(resolve, millis));
}
const object = Object.create({}, {
myProperty: asyncProperty({
async get() {
await time(1000);
return 'My value';
},
async set(value) {
await time(5000);
console.log('new value is', value);
}
})
});
Once you've set up with an asynchronous property like the above, you can set it as already illustrated:
(async function() {
console.log('getting...');
console.log('value from getter is', await object.myProperty);
console.log('setting...');
await object.myProperty('My new value');
console.log('done');
})();
The following allows for async setters in proxy handlers following the convention in Davide Cannizzo's answer.
var obj = new Proxy({}, asyncHandler({
async get (target, key, receiver) {
await new Promise(a => setTimeout(a, 1000))
return target[key]
},
async set (target, key, val, receiver) {
await new Promise(a => setTimeout(a, 1000))
return target[key] = val
}
}))
await obj.foo('bar') // set obj.foo = 'bar' asynchronously
console.log(await obj.foo) // 'bar'
function asyncHandler (h={}) {
const getter = h.get
const setter = h.set
let handler = Object.assign({}, h)
handler.set = () => false
handler.get = (...args) => {
let promise
return new Proxy(()=>{}, {
apply: (target, self, argv) => {
return setter(args[0], args[1], argv[0], args[2])
},
get: (target, key, receiver) => {
if (key == 'then' || key == 'catch') {
return callback => {
if (!promise) promise = getter(...args)
return promise[key](callback)
}
}
}
})
}
return handler
}
Here's another approach to this. It creates an extra wrapper, but in other aspects it covers what one would expect, including usage of await (this is TypeScript, just strip the : Promise<..> bit that sets return value type to get JS):
// this doesn't work
private get async authedClientPromise(): Promise<nanoClient.ServerScope> {
await this.makeSureAuthorized()
return this.client
}
// but this does
private get authedClientPromise(): Promise<nanoClient.ServerScope> {
return (async () => {
await this.makeSureAuthorized()
return this.client
})()
}
Here's how you could implement your get orders function
function get(name) {
return new Promise(function(resolve, reject) {
db.find("orders", {customer: name}, function(err, data) {
if (err) reject(err);
else resolve(data);
});
});
}
You could call this function like
customer.get("John").then(data => {
// Process data here...
}).catch(err => {
// Process error here...
});
How can i use the value of a promise to add it to an object property? If anyone can help me ill appreciate it.
//dolar.js
const axios = require('axios');
const url ='https://s3.amazonaws.com/dolartoday/data.json';
async function getTasaDolar(priceBs) {
axios.get(url).then(function (response) {
return response.data.USD.transferencia;
}).catch(function(error) {
}).then(function(response) {{
console.log('--------');
console.log(priceBs/response);
return priceBs/response;
}});
}
module.exports = getTasaDolar;
//object.js
const getTasaDolar = require('./dolar.js');
async function myFunc ({name, brand, category, priceBs}) {
const object = {
name,
brand,
category,
priceBs,
priceDolar : getTasaDolar(priceBs)
}
return object;
}
const hola = myFunc({name:`hola`, brand: `Chocozuela`, category:`Carnes`, priceBs:645000});
console.log(hola);
When I run this the output is :
Promise {
{ name: 'hola',
brand: 'Chocozuela',
category: 'Carnes',
priceBs: 645000,
priceDolar: Promise { undefined } } }
--------
3.467229680260142.
As you can see the value 3.46 is consoled.log but when i console.log the hola object the property priceDolar is Promise {undefined}
Simply declaring a function as async is not enough; you still have to await the Promises before you can use their results.
First problem is that getTasaDolar is not awaiting anything. To fix:
async function getTasaDolar(priceBs) {
const response = await axios.get(url);
return priceBs / response.data.USD.transferencia;
}
Now you also have to await this in myFunc:
async function myFunc ({name, brand, category, priceBs}) {
return {
name,
brand,
category,
priceBs,
priceDolar: await getTasaDolar(priceBs)
};
}
And yes, asynchronicity has bubbled up to myFunc as well, so if you want to call that you have to use await or a Promise callback:
myFunc({name:`hola`, brand: `Chocozuela`, category:`Carnes`, priceBs:645000})
.then(hola => console.log(hola));
Perhaps you had what seems to be a common misconception that making a function async tells JavaScript to automatically wait for async things, but all it really means is you can use the keyword await, which is a more convenient way to call the .then(...), .catch(...) and .finally(...) Promise methods.
There are 2 problems here:
1) getTasaDolar returns undefined because it doesn't explicitly return anything
2) You do not wait for the Promise to resolve when calling getTasaDolar
The getTasaDolar doesn't need to be async because it doesn't use await. Also there's no need for a catch since whoever is calling it can handle that. You were consuming the error which would cause the response to be undefined. Here's how I'd adjust this function:
function getTasaDolar(priceBs) {
return axios
.get(url)
.then(function(response) {
const data = response.data.USD.transferencia;
console.log("--------");
console.log(priceBs / data);
return priceBs / data;
});
}
The myFunc function is good to be async and you should await the result of the first function. But where you call myFunc has to use .then to see the returned value because myFunc is async and therefore returns a Promise object.
async function myFunc({ name, brand, category, priceBs }) {
const object = {
name,
brand,
category,
priceBs,
priceDolar: await getTasaDolar(priceBs) // <-- await here
};
return object;
}
myFunc({name:`hola`, brand: `Chocozuela`, category:`Carnes`,
priceBs:645000}).then(hola => console.log(hola);
I would like to create an RxJS Observable from an iterable like the following:
const networkIterableFactory = (resource: string) => {
let i = 0;
return {
[Symbol.iterator]() {
return {
next() {
return {
done: false,
value: fetch(resource, {
mode: 'cors',
}).then(async response => {
console.log('i = ', i);
await throttle(10000); // Do some stuff
i++;
return {i: 'i'};
}),
};
},
};
},
};
};
function throttle(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let networkIterable = networkIterableFactory('google.com');
let network$ = rxjs.from(networkIterable).pipe(rxjs.operators.take(5));
network$.subscribe(() => console.log('yo!'));
Issue is that i prints 5 times as 0. It seems as though the way that the iterable's iterator saves its state is through updating the outer closure. rxjs.from just takes the whole iterable as one emmission so a bunch of unresolved promises are returned, but I need the iterator state to be altered by logic within the promise callback. Is there a way to make the observable wait until the promise resolves before emitting the next item from the iterator? I would rather avoid using asyncIterable because I don't want to bring in IxRx.
Since the values of your iterable are returned asynchronously, then you should implement Symbol.asyncIterator instead of Symbol.iterator Try this instead:
const networkIterableFactory = (resource: string) => {
let i = 0;
return {
[Symbol.asyncIterator]() {
return {
next() {
return fetch(resource, { mode: 'cors' }).then(x => ({ done: false, value: x }));
},
};
},
};
};
function throttle(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let networkIterable = networkIterableFactory('google.com');
let network$ = rxjs.from(networkIterable).pipe(rxjs.operators.take(5));
network$.subscribe(() => console.log('yo!'));
Edit:
RxJS actually doesn't support Async iterators yet: https://github.com/ReactiveX/rxjs/issues/1624.
I also tried with an AsyncGenerator:
const { from } = require('rxjs');
async function* test() {};
const asyncGenerator = test();
from(asyncGenerator);
But it throws:
TypeError: You provided an invalid object where a stream was expected.
You can provide an Observable, Promise, Array, or Iterable.
So you won't be able to make it with this pattern, I actually believe that RxJS is not suited for "pulling" data like you do with take (If this pattern worked, it would end up in an infinite requests loop, even if you only take 5 results). It is rather designed to "push" things to whoever listens.
I'm trying to do asynchronous setters using async/await keywords.
Here some fakes database functions that takes time
function getProjectFromDatabase() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('My cool project!'), 500) // 500ms latency
});
}
function setProjectToDatabase(projectName) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('set!'), 500) // 500ms latency
});
}
Here is an example of implementation for the user
let user = {
// Getter
get project() {
return (async () => {
return await getProjectFromDatabase();
})();
},
// Setter
set project(projectName) {
return (async () => {
return await setProjectToDatabase(projectName);
})();
},
// Method
setProject(projectName) {
return (async () => {
return await setProjectToDatabase(projectName);
})();
}
};
And here is an example of use
(async function() {
console.log(await user.project); // Getter works!
await user.setProject('My new cool project!'); // Method works!
await (user.project = 'Another project'); // Setter doesn't work...
})();
But the return value from the setter function seems ignored.
How could I do that?
The assignment expression always evaluates to its right-hand side.
a.b.c = "This is what it gets evaluated to"
there is no way to change that.
Normally a setter sets some value and you don't care about the return value. In a traditional object this would be some other property (probably not intended to be used directly). Translated to something like a DB, the setter would post an INSERT or UPDATE and the new value would be set.
Your code in the setter isn't actually setting anything. If this were an actual DB call you would save some value in the database and then the next call to the getter would produce the new value. If you add something like this to the fan DB call, you will get something closer to traditional getter/setter logic:
const a_user = {project: 'My cool project!'} // some fake db object
function getProjectFromDatabase() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(a_user.project), 500) // 500ms latency
});
}
function setProjectToDatabase(projectName) {
return new Promise((resolve, reject) => {
setTimeout(() => {
a_user.project = projectName // actually set something
resolve(a_user.project) // caller of setter doesn't care about return value, but we still need to resolve so async works.
}, 500) // 500ms latency
});
}
let user = {
// Getter
get project() {
return (async () => {
return await getProjectFromDatabase();
})();
},
// Setter
set project(projectName) {
return (async () => {
return await setProjectToDatabase(projectName);
})();
},
};
(async function() {
console.log(await user.project); // Getter works!
await (user.project = 'Another project'); //
console.log(await user.project); // new value has been set
})();
Recently I was shown a piece of code that were asked during a full-stack developer interview.
It involved creating a Promise, in which the candidate should implement,
passing it a resolve function, and chaining 2 then's.
I tried implementing the Promise very naively only to make the code work.
Created a ctor that accepts a resolver func,
Created a Then function that accepts a callback and returns a Promise,
and simply calls the callback on the resolver function.
class MyPromise {
constructor(resolver) {
this.resolver = resolver;
}
then(callback) {
const result = new MyPromise(callback);
this.resolver(callback);
return result;
}
}
promise = new MyPromise(
(result) => {
setTimeout(result(2), 500);
});
promise.then(result => {
console.log(result);
return 2 * result;
}).then(result => console.log(result));
The expected result is 2,4 - just like operating with real Promise.
But i'm getting 2,2.
I'm having trouble figuring out how to get the return value for the first "then" and passing it along.
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));
Your question has some issues:
The r2 variable is nowhere defined. I will assume result was intended.
The setTimeout is doing nothing useful, since you execute result(2) immediately. I will assume setTimeout(() => result(2), 500) was intended.
If the code was really given like that in the interview, then it would be your job to point out these two issues before doing anything else.
One issue with your attempt is that the promise returned by the then method (i.e. result) is never resolved. You need to resolve it as soon as the this promise is resolved, with the value returned by the then callback.
Also, the promise constructor argument is a function that should be executed immediately.
In the following solution, several simplifications are made compared to a correct Promise behaviour.
It does not call the then callbacks asynchronously;
It does not support multiple then calls on the same promise;
It does not provide the rejection path;
It does not prevent a promise from resolving twice with a different value;
It does not deal with the special case where a then callback returns a promise
console.log("Wait for it...");
class MyPromise {
constructor(executor) {
executor(result => this.resolve(result));
}
resolve(value) {
this.value = value;
this.broadcast();
}
then(onFulfilled) {
const promise = new MyPromise(() => null);
this.onFulfilled = onFulfilled;
this.resolver = (result) => promise.resolve(result);
this.broadcast();
return promise;
}
broadcast() {
if (this.onFulfilled && "value" in this) this.resolver(this.onFulfilled(this.value));
}
};
// Code provided by interviewer, including two corrections
promise = new MyPromise(
(result) => {
setTimeout(()=>result(2), 500); // don't execute result(2) immediately
});
promise.then(result => {
console.log(result); // Changed r2 to result.
return 2 * result;
}).then(result => console.log(result));
Note the 500ms delay in the output, which is what should be expected from the (corrected) setTimeout code.
I posted a full Promises/A+ compliant promise implementation with comments in this answer
How about very simple:
const SimplePromise = function(cb) {
cb(
data =>
(this.data = data) &&
(this.thenCb || []).forEach(chain => (this.data = chain(this.data))),
error =>
(this.error = error) &&
(this.catchCb || []).forEach(chain => (this.error = chain(this.error)))
);
this.then = thenCb =>
(this.thenCb = [...(this.thenCb || []), thenCb]) && this;
this.catch = catchCb =>
(this.catchCb = [...(this.catchCb || []), catchCb]) && this;
};
Example Here: https://codesandbox.io/s/0q1qr8mpxn
Implying that this r2 is actually the result parameter.
The problem with your code is that you do not retrieve the result from the result(2). The first "then" gets executed, prints 2, return 4, but this 4 is just wasted.
I wrote some code with synchronous function only to demonstrate what to do if you want to get this 2,4 output:
class MyPromise {
constructor(resolver) {
this.resolver = resolver;
}
then(callback) {
var res = callback(this.resolver());
var result = new MyPromise(() => { return res; });
return result;
}
}
let promise = new MyPromise(
() => {
return 2;
});
promise
.then(result => {
console.log(result);
return 2 * result;
})
.then(result => {
console.log(result);
});
If you want the resolver to be somewhat async you should use Promises (because return value from function executed inside setTimeout can be retrieved, see
here.
If you are not allowed to use these built-in Promises, you can write them yourself with some dummy deferred object and await them (setInterval) to resolve (should be basically the same logic).
There are a few problems with your original code. Notably you are only executing the constructor argument upon call of the then method and you aren't actually chaining the outputs of the 'then' callbacks.
Here is a very (very!) basic promise implementation based upon adapting your example. It will also work for cases where 'then' is called after the promise is resolved (but not if 'then' is already called - multiple then blocks is not supported).
class MyPromise {
constructor(resolver) {
let thisPromise = this;
let resolveFn = function(value){
thisPromise.value = value;
thisPromise.resolved = true;
if(typeof thisPromise.thenResolve === "function"){
thisPromise.thenResolve();
}
}
if (typeof resolver === "function") {
resolver(resolveFn);
}
}
then(callback) {
let thisPromise = this;
thisPromise.thenFn = callback;
return new MyPromise((resolve) =>{
thisPromise.thenResolve = () => {
thisPromise.value = thisPromise.thenFn(thisPromise.value);
resolve(thisPromise.value);
}
//automatically resolve our intermediary promise if
//the parent promise is already resolved
if(thisPromise.resolved){
thisPromise.thenResolve();
}
});
}
};
//test code
console.log("Waiting for Godot...");
promise = new MyPromise((resolve) =>{
setTimeout(()=>{
resolve(2)
},500);
});
promise.then((result) => {
console.log(result);
return 2 * result;
}).then((result) => {
console.log(result);
return 2 * result;
}).then((result) => {
console.log(result)
});