Skip to content

Instantly share code, notes, and snippets.

@zhongyangxun
Last active March 13, 2021 13:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zhongyangxun/f11a89c066209952383cf58a9116a31f to your computer and use it in GitHub Desktop.
Save zhongyangxun/f11a89c066209952383cf58a9116a31f to your computer and use it in GitHub Desktop.
手写 Promise.
// 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