Hand tear Promise
When it comes to looking for an internship, I don't think it's very good to look at the experience. I can remember the best thing by writing code
Hand tear target
- Including then and catch methods
- Support chain call
- Asynchronous solution
Start hand tearing
First of all, if you don't know Promise, you may need to take a detour to learn it first, because you don't want to be wordy here, you have to write it directly
I think it's different from many online tutorials, because I didn't think of your ideas for the moment
Basic framework
const PENDING = "pending"; const FINISHED = "fulfilled"; const FAILED = "rejected"; class XPromise { constructor(handleFinish) { let __PromiseState__ = PENDING; this.__PromiseState__=__PromiseState__ this.onResolvedCallbacks = []; this.onRejectCallback = undefined; this.__PromiseResult__ = undefined; const resolve = (val) => { ... }; const reject = (val) => { ... }; try { handleFinish?.(resolve, reject); } catch (error) { reject(error); } } then(fun,handleError) { ... } catch(fun) { ... } }
Explain:
- __ PromiseState__ We use this variable to store the state of the Promise object, and the four states of "pending", "fully" and "rejected" are declared in advance
- onResolvedCallBacks this is a function array containing functions from then() that have not yet been executed. The array is used to ensure chain calls
- onRejectCallback stores a handleError function, which is executed when Promise throws an exception
- __ PromiseResult__ The result of resolve or reject is stored
Improve resolve, reject, then and catch
- resolve and reject
const resolve = (val) => { this.__PromiseResult__ = val; this.__PromiseState__ = FINISHED; }; this.reject = (val) => { this.__PromiseResult__ = val; this.__PromiseState__ = FAILED; delete this.reject };
Here, reject is deleted from the object after a reject run, in order to comply with the reason why rejection cannot be modified after reject in Promise specification
- then and catch
then(fun,handleError) { try { if (this.__PromiseState__ === PENDING){ this.onResolvedCallbacks.push(fun); this.onRejectCallback = handleError||this.onRejectCallback; } else if (this.__PromiseState__ === FINISHED) { let res = fun?.(this.__PromiseResult__); if (res) this.__PromiseResult__ = res; }else if (this.__PromiseState__ === FAILED) handleError?.(this.__PromiseResult__) } catch (error) { this.reject?.(error); } return this; } catch(fun) { return this.then(undefined,fun) }
The first parameter of then is the function fun that should be executed by the state fully, and the latter is the second parameter of Promise then
At runtime, if the current state is pending, then fun will be stuffed into onResolvedCallbacks. If the current state is fully, fun will be executed directly. If fun has a return value, the return value will be used as a new value__ PromiseResult__
Using setter to implement our asynchronous
Object.defineProperties(this, { __PromiseState__: { set(e) { __PromiseState__ = e; if (e === FINISHED) { try { this.onResolvedCallbacks.forEach(item =>item?.(this.__PromiseResult__)) } catch (error) { this.reject(error); } } else if (e === FAILED) { if(this.onRejectCallback instanceof Function) this.onRejectCallback?.(this.__PromiseResult__) else throw new Error(this.__PromiseResult__) } }, get:()=>__PromiseState__ }, });
Here's an explanation. The reason why we defined an additional__ PromiseState__ Instead of using this__ PromiseState__ This is because calling this attribute inside getter also triggers getter
When we resolve or reject, the setter will be triggered, that is, the set here
At this point, we can execute our function according to the state
65 lines of complete code
const PENDING = "pending"; const FINISHED = "fulfilled"; const FAILED = "rejected"; class XPromise { constructor(handleFinish) { let __PromiseState__ = PENDING; this.__PromiseState__ = __PromiseState__; this.onResolvedCallbacks = []; this.__PromiseResult__ = undefined; const resolve = (val) => { this.__PromiseResult__ = val; this.__PromiseState__ = FINISHED; }; this.reject = (val) => { this.__PromiseResult__ = val; this.__PromiseState__ = FAILED; delete this.reject; }; Object.defineProperties(this, { __PromiseState__: { set(e) { __PromiseState__ = e; if (e === FINISHED) { try { this.onResolvedCallbacks.forEach((item) => { let res = item?.(this.__PromiseResult__); if (res) this.__PromiseResult__ = res; }); } catch (error) { this.reject(error); } } else if (e === FAILED) { if (this.onRejectCallback instanceof Function) this.onRejectCallback?.(this.__PromiseResult__); else throw new Error(this.__PromiseResult__); } }, get: () => __PromiseState__, }, }); try { handleFinish?.(resolve, this.reject); } catch (error) { this.reject?.(error); } } then(fun, handleError) { try { if (this.__PromiseState__ === PENDING) { this.onResolvedCallbacks.push(fun); this.onRejectCallback = handleError || this.onRejectCallback; } else if (this.__PromiseState__ === FINISHED) { let res = fun?.(this.__PromiseResult__); if (res) this.__PromiseResult__ = res; } else if (this.__PromiseState__ === FAILED) handleError?.(this.__PromiseResult__); } catch (error) { this.reject?.(error); } return this; } catch(fun) { return this.then(undefined, fun); } }
test
So let's run and see if it works
Test 1: asynchronous and chained calls
function test() { let p = new XPromise((rs, rj) => { setTimeout(() => { rs(2021); }, 1000); }) .then((res) => { console.log(res); return 2022; }) .then((res) => { console.log(res); }); } test();
Estimated output 2021, 2022
Test 2: exception thrown
function test() { let p = new XPromise((rs, rj) => { setTimeout(() => { rs(2021); }, 1000); }).then(res=>{ console.log(res) const i=1 i=2 }) } test();
As expected, 2021 will be output after 1s; Then, because we modify the const value in then, an exception will be thrown
Test 3: catch exception
function test() { let p = new XPromise((rs, rj) => { setTimeout(() => { rs(2021); }, 1000); }) .then((res) => { const i=1 i=0 return 2022; }) .catch(e=>{ console.log(e) }) } test();
Now that you've caught it, you won't be wrong in red
So now, the hand tear Promise is completed
more
Only the most basic functions of Promise are implemented here. As for methods like all and race, I won't write more