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.
.then()can be passed two callbacks. You don't have a.catch()method. Inside of.then(), you would not use the executor callback function again when you construct a new promise. That is called once and only once when the original promise is created.reject()takes a reason.fnin your case)