Last active
March 13, 2021 13:07
-
-
Save zhongyangxun/f11a89c066209952383cf58a9116a31f to your computer and use it in GitHub Desktop.
手写 Promise.
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
// Promise A+: https://promisesaplus.com/ | |
function MyPromise(fn) { | |
this.status = 'pending'; | |
this.value = null; | |
this.callbacks = []; | |
this.resolve = (newVal) => { | |
const fn = () => { | |
if (this.status !== 'pending') { | |
return; | |
} | |
if (newVal?.then && typeof newVal.then === 'function') { | |
newVal.then(this.resolve, this.reject); | |
return; | |
} | |
this.value = newVal; | |
this.status = 'fulfilled'; | |
this.handleCb(); | |
}; | |
setTimeout(fn); | |
}; | |
this.reject = (error) => { | |
const fn = () => { | |
if (this.status !== 'pending') { | |
return; | |
} | |
if (error?.then && typeof error.then === 'function') { | |
newVal.then(this.resolve, this.reject); | |
return; | |
} | |
this.value = error; | |
this.status = 'rejected'; | |
this.handleCb(); | |
}; | |
setTimeout(fn); | |
}; | |
fn(this.resolve, this.reject); | |
} | |
MyPromise.prototype.handle = function (callback) { | |
if (this.status === 'pending') { | |
this.callbacks.push(callback); | |
return; | |
} | |
const { onFufilled, resolve, onRejected, reject } = callback; | |
const { value } = this; | |
const cb = this.status === 'fulfilled' ? onFufilled : onRejected; | |
const next = this.status === 'fulfilled' ? resolve : reject; | |
if (!cb) { | |
next(value); | |
return; | |
} | |
let ret; | |
try { | |
ret = cb(value); | |
} catch (error) { | |
reject(error); | |
} | |
resolve(ret); | |
}; | |
MyPromise.prototype.handleCb = function () { | |
while (this.callbacks.length) { | |
const callback = this.callbacks.shift(); | |
this.handle(callback); | |
} | |
}; | |
MyPromise.prototype.then = function (onFufilled, onRejected) { | |
return new MyPromise((resolve, reject) => { | |
this.handle({ | |
onFufilled, | |
resolve, | |
onRejected, | |
reject, | |
}); | |
}); | |
}; | |
MyPromise.prototype.catch = function (onRejected) { | |
return this.then(null, onRejected); | |
}; | |
MyPromise.resolve = function (value) { | |
if (value instanceof MyPromise) { | |
return value; | |
} | |
if (value?.then && typeof value.then === 'function') { | |
return new Promise((resolve) => { | |
return value.then(resolve); | |
}); | |
} else if (value) { | |
return new Promise((resolve) => resolve(value)); | |
} else { | |
return new MyPromise((resolve) => resolve()); | |
} | |
}; | |
MyPromise.reject = function (value) { | |
return new Promise((resolve, reject) => reject(value)); | |
}; | |
MyPromise.all = function (arr) { | |
let counter = arr?.length; | |
const vals = []; | |
return new MyPromise((resolve, reject) => { | |
if (!counter) { | |
resolve([]); | |
} | |
function res(i, value) { | |
try { | |
if ( | |
value && | |
typeof value === 'object' && | |
typeof value.then === 'function' | |
) { | |
value.then((val) => { | |
res(i, val); | |
}, reject); | |
return; | |
} | |
vals[i] = value; | |
if (--counter === 0) { | |
resolve(vals); | |
} | |
} catch (error) { | |
reject(error); | |
} | |
} | |
for (let i = 0; i < arr.length; i++) { | |
res(i, arr[i]); | |
} | |
}); | |
}; | |
MyPromise.race = function (arr) { | |
return new Promise((resolve, reject) => { | |
try { | |
for (let i = 0; i < arr?.length; i++) { | |
var item = arr[i]; | |
if ( | |
item && | |
typeof item === 'object' && | |
typeof item.then === 'function' | |
) { | |
item.then(resolve, reject); | |
continue; | |
} | |
resolve(item); | |
} | |
} catch (error) { | |
reject(error); | |
} | |
}); | |
}; | |
// new MyPromise((resolve, reject) => { | |
// setTimeout(() => { | |
// reject('reason'); | |
// }, 100); | |
// }) | |
// .then( | |
// (value) => { | |
// console.log('then 1', value); | |
// return 'then1ret'; | |
// }, | |
// (error) => { | |
// console.log('catch 1', error); | |
// return 'catch1ret'; | |
// } | |
// ) | |
// .then( | |
// (value) => { | |
// console.log('then 2', value); | |
// return 'then2ret'; | |
// }, | |
// (error) => { | |
// console.log('catch 2', error); | |
// return 'catch2ret'; | |
// } | |
// ).finally(() => { | |
// console.log('finally'); | |
// }) | |
// MyPromise.resolve('xxxxxx').then((value) => { | |
// console.log('then 1 value', value); | |
// }) | |
// MyPromise.reject('it is a reject').catch((error) => { | |
// console.log('catch 1 error', error); | |
// }) | |
var p1 = new MyPromise((resolve) => { | |
setTimeout(() => { | |
resolve(10000); | |
}, 10 * 1000); | |
}); | |
var p2 = new MyPromise((resolve) => { | |
setTimeout(() => { | |
resolve(5000); | |
}, 5 * 1000); | |
}); | |
MyPromise.all([p1, p2]).then((arr) => { | |
console.log('race then', arr); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment