I'm trying to build my own Promise to improve my understanding of Promise in javascript. I'm currently stuck at the .then
method and I'd like to ask: In the documentation for .then
here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then , it says that the then
method returns a Promise. I'm having difficulties writing this because it seem like this.result
is undefined
. Why is that and how do I fix this?
Here's my code:
class LitePromise {
constructor(fn) {
if (typeof fn !== "function") {
throw new TypeError("Promises have to be functions");
}
this.fn = fn;
this.state = PENDING;
this.result = null;
const resolveCallback = this.resolve.bind(this);
const rejectCallback = this.reject.bind(this);
try {
fn(resolveCallback, rejectCallback);
} catch (err) {
this.reject(err);
}
}
resolve(arg) {
console.log(arg, "arg");
this.result = arg;
this.state = RESOLVED;
}
reject() {
this.state = REJECTED;
}
// make an instance of this promise
then(callback) {
const tinyPromise = new LitePromise(this.fn);
console.log(callback, "callback");
try {
return tinyPromise.resolve(callback(this.result));
} catch {
return tinyPromise.reject(callback(this.result));
}
}
}
console.log("------------------");
const yaypromise = new LitePromise((resolve, reject) => {
console.log("1. running");
setTimeout(() => {
console.log("2. resolving");
resolve("yay");
}, 100);
}).then((result) => {
console.log("3. evaluating");
if (result !== "yay") {
console.log("Is not working yet");
return;
}
console.log("SUCCESS!", result);
});
I think the core issue here is the following. then()
typically gets processed in 2 different ways:
then()
and call those callbacks when the promise gets resolved later . You're never handling case 1, so if the promise resolves after then()
gets called, it won't work.
In addition, the sub promise you return from then()
should itself resolve one the result of the callback passed to then()
completes.
If this sounds super confusing, it's because it's hard =) I would recommend to try and get your logic right first for just dealing with the callbacks in .then()
, and don't return anything from .then()
yet .
I also made my own promise for the same reason. It's pretty minimal, maybe it's helpful:
https://github.com/evert/promise-demo/blob/master/src/my-promise.js
Here is a sample program we expect to work with our promise
; note the lowercase p
-
const delay = ms =>
new promise(r => setTimeout(r, ms))
const roll = n =>
{ console.log("rolling...")
return delay(1000)
.then(_ => Math.ceil(Math.random() * n))
.then(x => { console.log(x); return x })
}
const main = _ =>
roll(20).then(x =>
roll(20).then(y =>
roll(20).then(z =>
{ console.log("three rolls:", x, y, z)
return [x,y,z]
}
)
)
)
main()
.then(([x,y,z]) => x + y + z)
.then(sum => console.log("sum:", sum))
.catch(console.error)
rolling...
12
rolling...
18
rolling...
15
three rolls: 12 18 15
sum: 45
As others commented there is a lot to fix in your code. But don't feel bad as Promise is not particularly easy to implement. Here's my first sketch of promise
. Note value
, resolved
, rejected
are parameters of the constructor but they should be made private. This can be done using a variety of techniques, but for sake of simplicity I left it this way for now. The caller is only intended to pass the first argument, exec
-
class promise
{ constructor(exec, value, resolved = false, rejected = false)
{ this.value = value
this.resolved = resolved
this.rejected = rejected
this.callback = []
if (this.resolved || this.rejected) return
exec(x => this.resolve(x), e => this.reject(e))
}
resolve(value)
{ if (this.resolved || this.rejected) return
let p = promise.resolve(value)
for (const [ifResolved, ifRejected] of this.callback)
p = p.then(ifResolved, ifRejected)
Object.assign(this, p)
}
reject(value)
{ if (this.resolved || this.rejected) return
let p = promise.reject(value)
for (const [ifResolved, ifRejected] of this.callback)
p = p.then(ifResolved, ifRejected)
Object.assign(this, p)
}
then(ifResolved, ifRejected = promise.reject)
{ if (this.resolved)
{ try
{ return promise.resolve(ifResolved(this.value)) }
catch (err)
{ return promise.reject(err) }
}
else if (this.rejected)
{ try
{ return promise.resolve(ifRejected(this.value)) }
catch (err)
{ return promise.reject(err) }
}
else
{ this.callback.push([ifResolved, ifRejected])
return this
}
}
catch(ifRejected)
{ return this.then(value => value, ifRejected) }
static resolve(value)
{ return (value instanceof promise)
? value
: new promise(_ => {}, value, true, false)
}
static reject(value)
{ return (value instanceof promise)
? value
: new promise(_ => {}, value, false, true)
}
}
Like Promise, only p.then
, p.catch
should be available on promise
objects. We should prevent the user from calling p.resolve
or p.reject
directly, but for now it makes it easy to see how things work. The class functions promise.resolve
and promise.reject
are analogous to Promise.resolve
and Promise.reject
.
Expand the snippet below to verify the result in your own browser -
class promise { constructor(exec, value, resolved = false, rejected = false) { this.value = value this.resolved = resolved this.rejected = rejected this.callback = [] if (this.resolved || this.rejected) return exec(x => this.resolve(x), e => this.reject(e)) } resolve(value) { if (this.resolved || this.rejected) return let p = promise.resolve(value) for (const [ifResolved, ifRejected] of this.callback) p = p.then(ifResolved, ifRejected) Object.assign(this, p) } reject(value) { if (this.resolved || this.rejected) return let p = promise.reject(value) for (const [ifResolved, ifRejected] of this.callback) p = p.then(ifResolved, ifRejected) Object.assign(this, p) } then(ifResolved, ifRejected = promise.reject) { if (this.resolved) { try { return promise.resolve(ifResolved(this.value)) } catch (err) { return promise.reject(err) } } else if (this.rejected) { try { return promise.resolve(ifRejected(this.value)) } catch (err) { return promise.reject(err) } } else { this.callback.push([ifResolved, ifRejected]) return this } } catch(ifRejected) { return this.then(value => value, ifRejected) } static resolve(value) { return (value instanceof promise)? value: new promise(_ => {}, value, true, false) } static reject(value) { return (value instanceof promise)? value: new promise(_ => {}, value, false, true) } } const delay = ms => new promise(r => setTimeout(r, ms)) const roll = n => { console.log("rolling...") return delay(1000).then(_ => Math.ceil(Math.random() * n)).then(x => { console.log(x); return x }) } const main = _ => roll(20).then(x => roll(20).then(y => roll(20).then(z => { console.log("three rolls:", x, y, z) return [x,y,z] } ) ) ) main().then(([x,y,z]) => x + y + z).then(sum => console.log("sum:", sum)).catch(console.error)
I will come back tomorrow to add an example that demonstrates errors and answer any questions if you have them.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.