The first edition

class MyPromise{ constructor(excutor){ this.state = "pending"; this.resolveCallbacks = []; this.rejectCallbacks = []; let resolve = (value)=>{ if(this.state==="pending"){ this.state = "resolved"; this.resolveCallbacks.forEach(fn=>fn(value)); }}; let reject = (reason)=>{ if(this.state==="pending"){ this.state = "rejected"; this.rejectCallbacks.forEach(fn=>fn(reason)); }}; try{ excutor(resolve, reject); }catch(e){ console.log("excutor error", e); reject(e); } } then(resolve, reject){ this.resolveCallbacks.push(resolve); this.rejectCallbacks.push(reject); } } getAjax("http://localhost:8081/getAPITest").then(res=>{ console.log(res); }, error=>{ console.log("error", error); }) function getAjax(url){ return new MyPromise((resolve, reject)=>{ let xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onreadystatechange = ()=>{ if(xhr.readyState ! ==4){ return; } if(xhr.status ===200){ resolve(xhr.response); }else{ reject(new Error(xhr.status)); } } xhr.send(); })}Copy the code

The first version of the application had no problem executing asynchronous Ajax requests. However, when executing synchronized code, the methods defined in then do not execute:

let test = new MyPromise((resolve, reject)=>{ console.log("start"); resolve("value"); }); Test. then(res=>{// console.log(res) is not executed; })Copy the code

Why is that? This starts with the order of execution. When we new MyPromise, we execute the passed function (fn)(resolve, reject)=>{}. At the same time resolve (” value “); Because in the function fn, which is defined, it will also be executed. There are no Ajax requests and they are executed synchronously. When we call test. Then () is also synchronized code, this time will trigger this. ResolveCallbacks. Push (resolve); But the Resolve method has already been executed. The callback inside then will not be called. Solution:

  • Solution a:

    The then method defined in MyPromise is divided into three cases:
    Then (resolve, reject){if(this.state==="resolved"){resolve(this.value); If (this.state=== ="reject "){reject(this.reason); } / / when the ajax request is then method to resolve first perform the if (this. State = = = "pending") {this. ResolveCallbacks. Push (resolve); this.rejectCallbacks.push(reject); }}Copy the code

    referenceJuejin. Cn/post / 684490…

  • Scheme two uses MutationObserver (microtask) to mimic nextTick and rewrite the Resolve method. MutationObserver executes after all the code in the same step has been executed. Look at the following code:
Function nextTick(fn) {// Implement nextTick var counter = 1; var observer = new MutationObserver(fn); var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true, }); counter += 1; textNode.data = String(counter); } setTimeout(() => { console.log("setTimeout") }, 0); nextTick(()=>{ console.log("nextTick"); }) console.log("wrap");Copy the code

It prints: sync code: wrap, then microtask: nextTick, and finally macro task setTimeout. So, we use the microtask MutationObserver to override the resolve method so that resolve executes last. ensure

Class MyPromise{... class MyPromise{... class MyPromise{... Other code then (resolve, reject) {this. ResolveCallbacks. Push (resolve); this.rejectCallbacks.push(reject); } let resolve = (value)=>{if(this.state==="pending"){console.log("resolve") this.state  = "resolved"; this.value = value; nextTick(()=>{ this.resolveCallbacks.forEach(fn=>fn(value)); }}}}; Function nextTick(fn) {// Implement nextTick var counter = 1; var observer = new MutationObserver(fn); var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true, }); counter += 1; textNode.data = String(counter); }Copy the code

Reference: mp.weixin.qq.com/s/eoo3WLqGN…

The second edition

Plan a

class MyPromise{ constructor(excutor){ this.state = "pending"; this.resolveCallbacks = []; this.rejectCallbacks = []; this.value = ""; this.reason = ""; let resolve = (value)=>{ if(this.state==="pending"){ this.state = "resolved"; this.value = value; this.resolveCallbacks.forEach(fn=>fn(value)); }}; let reject = (reason)=>{ if(this.state==="pending"){ this.state = "rejected"; this.reason = reason; this.rejectCallbacks.forEach(fn=>fn(reason)); }}; try{ excutor(resolve, reject); }catch(e){ console.log("excutor error", e); reject(e); If (this.state==="resolved"){resolve(this.value); If (this.state=== ="reject "){reject(this.reason); } / / when the ajax request is then method to resolve first perform the if (this. State = = = "pending") {this. ResolveCallbacks. Push (resolve); this.rejectCallbacks.push(reject); }}}Copy the code

Scheme 2

class MyPromise{ constructor(excutor){ this.state = "pending"; this.resolveCallbacks = []; this.rejectCallbacks = []; this.value = ""; this.reason = ""; let resolve = (value)=>{ if(this.state==="pending"){ console.log("resolve") this.state = "resolved"; this.value = value; nextTick(()=>{ this.resolveCallbacks.forEach(fn=>fn(value)); })}}; let reject = (reason)=>{ if(this.state==="pending"){ this.state = "rejected"; this.reason = reason; nextTick(()=>{ this.rejectCallbacks.forEach(fn=>fn(reason)); })}}; try{ excutor(resolve, reject); }catch(e){ console.log("excutor error", e); reject(e); } } then(resolve, reject){ this.resolveCallbacks.push(resolve); this.rejectCallbacks.push(reject); }} function nextTick(fn) {var counter = 1; var observer = new MutationObserver(fn); var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true, }); counter += 1; textNode.data = String(counter); }Copy the code