Last active
July 2, 2017 06:02
-
-
Save zyf0330/01f2115ba32e031c018f77b898628e4c to your computer and use it in GitHub Desktop.
My Promise implement. Must run in Node.js. 只是一个没有按照标准实现的残次品
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let id = 1 | |
function Promise(executor) { | |
this.id = id++ | |
this.status = 'pending' | |
this.value = null | |
this._next = null | |
const response = (status, v) => { | |
if (this.status != 'pending') { | |
return | |
} | |
this.value = v | |
this.status = status | |
setTimeout(() => { | |
this._run() | |
}) | |
} | |
const res = (v) => { | |
response('resolved', v) | |
} | |
const rej = (v) => { | |
response('rejected', v) | |
} | |
this._run = () => { | |
let _next = this._next | |
let expectNext = null | |
const expectType = this.status == 'resolved' ? 'then' : 'catch' | |
while (expectNext == null && _next != null) { | |
if (_next.type == expectType) { | |
expectNext = _next | |
} else { | |
_next = _next.p._next | |
} | |
} | |
if (expectNext != null) { | |
if (expectNext.called) return | |
expectNext.called = true | |
let result | |
try { | |
result = expectNext.func(this.value) | |
} catch (e) { | |
return expectNext.rej(e) | |
} | |
if (result instanceof Promise == false) { | |
result = expectNext.res(result) | |
} else { | |
result.then(expectNext.res).catch(expectNext.rej) | |
} | |
} else if (this.status == 'rejected') { | |
throw this.value | |
} | |
} | |
try { | |
executor(res, rej) | |
} catch (e) { | |
rej(e) | |
} | |
} | |
Promise.prototype.chain = function (type, func) { | |
const _next = this._next = {type: type, called: false} | |
const p = new Promise(function (res, rej) { | |
_next.res = res | |
_next.rej = rej | |
}) | |
_next.func = func | |
_next.p = p | |
setImmediate(() => { | |
this._run() | |
}) | |
if (type == 'catch') { | |
if (unhandledPromiseError != null) { | |
process.nextTick(() => { | |
console.error('async promise error handle', unhandledPromiseError); | |
unhandledPromiseError = null | |
}) | |
} | |
} | |
return p | |
} | |
Promise.prototype.then = function (func) { | |
return this.chain('then', func) | |
} | |
Promise.prototype.catch = function (func) { | |
return this.chain('catch', func) | |
} | |
Promise.prototype.toString = function () { | |
return `Promise <${this.status} ${this.value}>` | |
} | |
Promise.resolve = (v) => { | |
const p = new Promise((res) => res(v)) | |
return p | |
} | |
Promise.reject = (v) => { | |
const p = new Promise((res, rej) => rej(v)) | |
return p | |
} | |
Promise.all = (promises) => { | |
let finish = 0 | |
const results = [] | |
return new Promise((res, rej) => { | |
promises.forEach((promise, i) => | |
promise | |
.then((v) => { | |
results[i] = v; | |
finish += 1 | |
if (finish == promises.length) { | |
res(results) | |
} | |
}) | |
.catch((e) => { | |
rej(e) | |
}) | |
) | |
}) | |
} | |
Promise.race = (promises) => { | |
let finish = false | |
return new Promise((res, rej) => { | |
promises.forEach((promise, i) => | |
promise | |
.then((v) => { | |
if (finish) { | |
return | |
} | |
finish = true | |
res(v) | |
}) | |
.catch((e) => { | |
if (finish) { | |
return | |
} | |
finish = true | |
rej(e) | |
}) | |
) | |
}) | |
} | |
let unhandledPromiseError = null | |
// TODO 这里应该使用 domain 来捕获错误 | |
process.on('uncaughtException', (err) => { | |
if (unhandledPromiseError != null) { | |
return | |
} | |
console.error('unhandled promise rejection:', err) | |
unhandledPromiseError = err | |
}); | |
module.exports = Promise |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment