preface
Promise is no stranger to JavaScript asynchronous flow from the original Callback, to Promise, to Generator, As far as Async/Await is concerned (if you are not familiar with these, please refer to my other article “JavaScript Asynchronous Programming”), this is not only the development of technical implementation, but also the progress of thinking about how to control asynchracy. Promises are the most important part of any follow-up plan, and they’re the one most often asked in interviews.
Today we will implement A Promise based on the A+ specification from 0 to 1. During the process, we will also discuss the exception handling of the Promise and whether it can be terminated manually. Finally, we will do unit tests on the Promise we implemented. The full code has been uploaded to Github. If you want to see the code directly, you can click here.
Although there are a lot of articles that make you realize the Promise class, everyone understands it differently, and maybe different articles can give you different thoughts. Let’s start.
The body of the
1. Basic framework
New Promise() accepts an executor function as an argument, which will execute immediately. Resolve and reject are two arguments that are also functions. Otherwise, error capture cannot be performed.
MyPromise.js
function MyPromise(executor) {
function resolve(value) {
}
function reject(reason) {
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
module.exports = MyPromise;
Copy the code
Resolve () Accept the Promise success value value, reject Accept the Promise failure cause Reason.
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
resolve(123);
})
Copy the code
2. Add a state machine
Current implementation problems:
- Promise is a state machine mechanism with an initial state of
pending
, the success status isfulfilled
, and the failure state isrejected
. Only frompending
->fulfilled
, or frompending
->rejected
And once the state is changed, it is never changed again.
So, we need to add a state flow mechanism for promises.
MyPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(executor) {
let self = this;
self.state = PENDING;
function resolve(value) {
if(self.state === PENDING) { self.state = FULFILLED; }}function reject(reason) {
if (self.state === PENDING) {
self.state = REJECTED;
}
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
module.exports = MyPromise;
Copy the code
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
resolve(123);
});
promise.then(function(value) {
console.log('value', value);
}, function(reason) {
console.log('reason', reason);
})
Copy the code
3. Addthen
methods
A Promise has a then method that accepts two functions, onFulfilled and onRejected, as the callback of the Promise’s success and failure, respectively. Therefore, in the then method, we need to judge the state. If this is fulfilled, the onFulfilled(value) method will be implemented; if this is fulfilled, the onFulfilled(reason) method will be implemented.
Since the success value value and the failure cause reason are passed in by the user in executor via resolve(value) and reject(Reason), we need to have a global value and reason for subsequent methods to fetch.
MyPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(executor) {
let self = this;
self.state = PENDING;
self.value = null;
self.reason = null;
function resolve(value) {
if(self.state === PENDING) { self.state = FULFILLED; self.value = value; }}function reject(reason) {
if (self.state === PENDING) {
self.state = REJECTED;
self.reason = reason;
}
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
if (self.state === FULFILLED) {
onFuifilled(self.value);
}
if(self.state === REJECTED) { onRejected(self.reason); }}; module.exports = MyPromise;Copy the code
4. Invoke Resolve asynchronously
Current implementation problems:
- A synchronous invocation
resolve()
No problem, but if it’s an asynchronous call like putsetTimeout
Because the current code is callingthen()
Method,state
Is stillpending
State, when the timer is calledresolve()
thestate
Modified tofulfilled
State, butonFulfilled()
The function has no time to call.
In view of the above problems, the following modifications are made:
MyPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(executor) {
let self = this;
self.state = PENDING;
self.value = null;
self.reason = null;
self.onFulfilledCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value) {
if (self.state === PENDING) {
self.state = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(function(fulfilledCallback) { fulfilledCallback(); }); }}function reject(reason) {
if (self.state === PENDING) {
self.state = REJECTED;
self.reason = reason;
self.onRejectedCallbacks.forEach(function(rejectedCallback) {
rejectedCallback();
});
}
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
if (self.state === PENDING) {
self.onFulfilledCallbacks.push(() => {
onFuifilled(self.value);
});
self.onRejectedCallbacks.push(() => {
onRejected(self.reason);
});
}
if (self.state === FULFILLED) {
onFuifilled(self.value);
}
if(self.state === REJECTED) { onRejected(self.reason); }}; module.exports = MyPromise;Copy the code
We added two callback function arrays, onledCallbacks and onRejectedCallbacks, to store success and failure callbacks passed in the then() method. Then, when the user calls resolve() or Reject (), the state is modified and the callback functions are executed one by one from the corresponding callback array.
In this way, we can also register multiple THEN () functions and execute them in the order of registration on success or failure.
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
setTimeout(function() {
resolve(123);
}, 1000);
});
promise.then(function(value) {
console.log('value1', value);
}, function(reason) {
console.log('reason1', reason);
});
promise.then(function(value) {
console.log('value2', value);
}, function(reason) {
console.log('reason2', reason);
});
Copy the code
5. Then returns a Promise
If you have read the PromiseA+ specification, you will know that the then() method still returns a Promise, and the resolve () function is the return value of the onFulfilled() or onRejected() function of the previous Promise. If an error occurs during the execution of the previous Promise’s then() method callback, it is caught and passed in as an argument to the onRejected function of the returned Promise. Such as:
let promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((value) => {
console.log('value1', value);
return 456;
}).then((value) => {
console.log('value2', value);
});
let promise = new Promise((resolve, reject) => {
resolve(123);
});
Copy the code
The printed result is:
value1 123
value2 456
let promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((value) => {
console.log('value1', value); a.b = 2; // There is a syntax errorreturn 456;
}).then((value) => {
console.log('value2', value);
}, (reason) => {
console.log('reason2', reason);
});
Copy the code
The printed result is:
value1 123
reason2 ReferenceError: a is not defined
As you can see, if an error occurs in the then() callback, the Promise returned by then() is automatically changed to onRejected(), and the onRejected() callback is executed.
let promise = new Promise((resolve, reject) => {
reject(123);
});
promise.then((value) => {
console.log('value1', value);
return 456;
}, (reason) => {
console.log('reason1', reason);
return 456;
}).then((value) => {
console.log('value2', value);
}, (reason) => {
console.log('reason2', reason);
});
Copy the code
The printed result is:
reason1 123
value2 456
Okay, so let’s implement thatthen()
The method still returns a Promise.
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
let promise2 = null;
promise2 = new MyPromise((resolve, reject) => {
if (self.state === PENDING) {
self.onFulfilledCallbacks.push(() => {
try {
letx = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch(reason) { reject(reason); }}); self.onRejectedCallbacks.push(() => { try {letx = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch(reason) { reject(reason); }}); }if (self.state === FULFILLED) {
try {
letx = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); }}if (self.state === REJECTED) {
try {
letx = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); }}});return promise2;
};
Copy the code
As you can see, we have added a promise2 as the return value of the THEN () method. Let x = onfuifle (self.value) or let x = onRejected(self.reason) to return the value of the then() callback function, Then call self.resolvePromise(promise2, x, resolve, reject) to pass the new resolve and reject of promise2, x, and promise2 into resolvePromise().
So let’s focus on thatresolvePromise()
Methods.
MyPromise.js
MyPromise.prototype.resolvePromise = function(promise2, x, resolve, reject) {
let self = this;
let called = false; // called prevents multiple callsif (promise2 === x) {
return reject(new TypeError('Circular reference'));
}
if(x ! == null && (Object.prototype.toString.call(x) ==='[object Object]' || Object.prototype.toString.call(x) === '[object Function]'// x is an object or a function try {let then = x.then;
if (typeof then= = ='function') {thene.call (x, (y) => {// Someone else's PromisethenMethod may set getters and so on, using called to prevent multiple callsthenmethodsif (called) return ;
called = true; // Success y may still be a promise or a promisethenResolvePromise (promise2, y, resolve, reject); }, (reason) => {if (called) return ;
called = true;
reject(reason);
});
} else {
if (called) return ;
called = true;
resolve(x);
}
} catch (reason) {
if (called) return ;
called = true; reject(reason); }}elseResolve resolve(x); }};Copy the code
ResolvePromise () is used to resolve that the then() callback returns a Promise, which may be our own, implemented by another library, or an object with a then() method. So resolvePromise() is used to implement the unified processing.
ResolvePromise () resolvePromise() :
Promise resolution process
Promise resolution is an abstract operation that takes a Promise and a value, which we express as [[Resolve]](Promise, x). If x has a then method and looks like a Promise, The resolver attempts to make the Promise accept x’s state; Otherwise it implements the promise with the value of x.
This thenable feature makes Promise implementation more universal: as long as it exposes A THEN method that follows the Promise/A+ protocol; This also allows implementations that follow the Promise/A+ specification to coexist well with less formal but usable implementations.
To run [[Resolve]](promise, x), follow these steps:
-
X is equal to a Promise. If a promise and x refer to the same object, reject the promise as TypeError
-
If x is a Promise, make the Promise accept the state of x:
- If X is in wait state, the promise needs to remain in wait state until x is executed or rejected
- If x is in the execution state, execute the promise with the same value
- If X is in the reject state, reject the promise with the same grounds
-
If x is an object or function:
- Assign x. teng to then
- If an error e is thrown when taking the value x. teng, reject the promise based on e
- If then is a function, x is called as the function’s scope this. Pass two callback functions as arguments, the first called resolvePromise and the second called rejectPromise:
- If resolvePromise is called with the value y, run [[Resolve]](promise, y)
- If rejectPromise is invoked with argument r, reject the promise with argument r
- If both resolvePromise and rejectPromise are invoked, or if the same parameter is invoked more than once, the first call is preferred and the remaining calls are ignored
- If calling the then method raises exception e:
- If a resolvePromise or rejectPromise has already been invoked, it is ignored
- Otherwise, reject the promise based on e
- If then is not a function, execute the promise with an x argument
- If x is not an object or function, execute the promise with x as an argument
If a promise is resolved by an object in a loop’s Thenable chain, and the recursive nature of [[Resolve]](promise, thenable) causes it to be called again, the algorithm above will lead to infinite recursion. The algorithm does not require it, but encourages the agent to detect the presence of such recursion, and if so, reject the promise with an identifiable TypeError as a justification.
With the above specification in mind and the comments in the code, you should be able to understand what resolvePromise() does.
Testing:
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
setTimeout(function() {
resolve(123);
}, 1000);
});
promise.then((value) => {
console.log('value1', value);
return new MyPromise((resolve, reject) => {
resolve(456);
}).then((value) => {
returnnew MyPromise((resolve, reject) => { resolve(789); })}); }, (reason) => { console.log('reason1', reason);
}).then((value) => {
console.log('value2', value);
}, (reason) => {
console.log('reason2', reason);
});
Copy the code
Print result:
value1 123
value2 789
6. Letthen()
Method callbacks are always called asynchronously
The callback function implemented by the official Promise is always called asynchronously:
console.log('start');
let promise = new Promise((resolve, reject) => {
console.log('step-');
resolve(123);
});
promise.then((value) => {
console.log('step--');
console.log('value', value);
});
console.log('end');
Copy the code
Print result:
start
step-
end
step–
value1 123
Promise belongs to micro-task. Here we use macro task setTiemout to replace asynchronous realization for convenience. For details about macro task, micro-task and Event Loop, you can refer to another article of mine to thoroughly understand Event Loop.
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
let promise2 = null;
promise2 = new MyPromise((resolve, reject) => {
if (self.state === PENDING) {
self.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
letx = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); }}, 0); }); self.onRejectedCallbacks.push(() => {setTimeout(() => {
try {
letx = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); }}, 0); }); }if (self.state === FULFILLED) {
setTimeout(() => {
try {
letx = onFuifilled(self.value); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); }}, 0); }if (self.state === REJECTED) {
setTimeout(() => {
try {
letx = onRejected(self.reason); self.resolvePromise(promise2, x, resolve, reject); } catch (reason) { reject(reason); }}, 0); }});return promise2;
};
Copy the code
Testing:
test.js
let MyPromise = require('./MyPromise.js');
console.log('start');
let promise = new MyPromise((resolve, reject) => {
console.log('step-');
setTimeout(() => {
resolve(123);
}, 1000);
});
promise.then((value) => {
console.log('step--');
console.log('value', value);
});
console.log('end');
Copy the code
Print result:
start
step-
end
step–
value1 123
Now that a basic Promise has been implemented, we will implement some extension methods that are not in the PromiseA+ specification.
7. To achievecatch()
methods
The ondepressing and onRejected callback functions of then() are not mandatory. If not, we cannot accept errors in reject(reason). In this case, we can use the catch() method to receive errors. For example:
let promise = new Promise((resolve, reject) => {
reject('has error');
});
promise.then((value) => {
console.log('value', value);
}).catch((reason) => {
console.log('reason', reason);
});
Copy the code
Print result:
reason has error
Not only that, catch() can be used as the last step in a chain call to a Promise, with previous Promise errors bubbling up into the last catch() to catch exceptions. For example:
let promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((value) => {
console.log('value', value);
return new Promise((resolve, reject) => {
reject('has error1');
});
}).then((value) => {
console.log('value', value);
return new Promise((resolve, reject) => {
reject('has error2');
});
}).catch((reason) => {
console.log('reason', reason);
});
Copy the code
Print result:
value 123
reason has error1
So how exactly does the catch() method work?
The onFulfilled and onRejected functions will have default values in the Promise implementation:
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) {
onFuifilled = typeof onFuifilled === 'function' ? onFuifilled : value => {returnvalue; }; onRejected = typeof onRejected ==='function' ? onRejected : reason => {throw reason};
};
MyPromise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};
Copy the code
As you can see, the default value of onRejected is throw error Reason. Since our execution of synchronized code is done in a try… If a Promise fails, the default function throws a reject(reject) error, which is captured by promise2 as a reject(Reason) resolution.
The catch() implementation calls this.then(null, onRejected), and the onRejected callback is executed because promise2 is rejected, thus catching the first promise error.
Then () does not pass the onRejected callback. By default, Promise will throw a callback, reject or try… The error reason is resolved by the promise2 as reject(reason), which is then called by the onRejected callback of the next THEN () method. Catch just writes a special THEN (null, onRejected).
Therefore, when we write the chain call of Promise, we can not pass the onRejected callback in then(). We only need to add a catch() at the end of the chain call. In this way, all errors in the chain will be caught by the last catch.
Example 1:
letpromise = new Promise((resolve, reject) => { reject(123); }); Promise.then ((value) => {// Note that this is not the case, because the first promise is a reject console. Log ()'value1', value);
return new Promise((resolve, reject) => {
reject('has error1');
});
}).then((value) => {
console.log('value2', value);
return new Promise((resolve, reject) => {
reject('has error2'); }); }, (reason) => {// notice thisthenThe onRejected callback console.log('reason2', reason); }). Catch ((reason) => {// Error on previous onethenIt gets caught, so it doesn't go to console.log('reason3', reason);
});
Copy the code
Print result:
reason2 123
Example 2:
let promise = new Promise((resolve, reject) => {
reject(123);
});
promise.then((value) => {
console.log('value1', value);
return new Promise((resolve, reject) => {
reject('has error1');
});
}).then((value) => {
console.log('value2', value);
return new Promise((resolve, reject) => {
reject('has error2'); }); }).catch((reason) => {// because in the chainthenThere is no onRejected callback, so it bubbles until the last catch'reason3', reason);
});
Copy the code
Catch, like then, returns a new Promise. Some of you may be wondering what happens if the catch callback also fails, which we’ll talk about later in the Promise exception handling section.
Print result:
reason3 123
8. To achievefinally
methods
Finally is an extension of the Promise implementation by some libraries, and both resolve and Reject follow the finally method.
MyPromise.js
MyPromise.prototype.finally = function(fn) {
return this.then(value => {
fn();
return value;
}, reason => {
fn();
throw reason;
});
};
Copy the code
9. To achievedone
methods
The done method, as the last step in the Promise chain call, is used to globally throw errors that were not caught internally by the Promise and never return a Promise again. Usually used to end a Promise chain.
MyPromise.js
MyPromise.prototype.done = function() {
this.catch(reason => {
console.log('done', reason);
throw reason;
});
};
Copy the code
10. ImplementationPromise.all
methods
Promise.all() receives an array containing multiple promises. When all promises are fulfilled, an array of results will be returned. The order of results in the array corresponds to the order of incoming promises one by one. If there is a Promise (rejected) state, then the whole promise. all is rejected.
MyPromise.js
MyPromise.all = function(promiseArr) {
return new MyPromise((resolve, reject) => {
let result = [];
promiseArr.forEach((promise, index) => {
promise.then((value) => {
result[index] = value;
if (result.length === promiseArr.length) {
resolve(result);
}
}, reject);
});
});
};
Copy the code
test.js
let MyPromise = require('./MyPromise.js');
let promise1 = new MyPromise((resolve, reject) => {
console.log('aaaa');
setTimeout(() => {
resolve(1111);
console.log(1111);
}, 1000);
});
let promise2 = new MyPromise((resolve, reject) => {
console.log('bbbb');
setTimeout(() => {
reject(2222);
console.log(2222);
}, 2000);
});
let promise3 = new MyPromise((resolve, reject) => {
console.log('cccc');
setTimeout(() => {
resolve(3333);
console.log(3333);
}, 3000);
});
Promise.all([promise1, promise2, promise3]).then((value) => {
console.log('all value', value);
}, (reason) => {
console.log('all reason', reason);
})
Copy the code
Print result:
aaaa
bbbb
cccc
1111
2222
all reason 2222
3333
11. To achievePromise.race
methods
Promise.race() receives an array containing multiple promises, and when one Promise is fulfilled, the whole big Promise is onfulfilled, and executes the onfulfilled callback function. If there is a Promise in the Rejected state, then the whole promise. race is rejected.
MyPromise.js
MyPromise.race = function(promiseArr) {
return new MyPromise((resolve, reject) => {
promiseArr.forEach(promise => {
promise.then((value) => {
resolve(value);
}, reject);
});
});
};
Copy the code
test.js
let MyPromise = require('./MyPromise.js');
let promise1 = new MyPromise((resolve, reject) => {
console.log('aaaa');
setTimeout(() => {
resolve(1111);
console.log(1111);
}, 1000);
});
let promise2 = new MyPromise((resolve, reject) => {
console.log('bbbb');
setTimeout(() => {
reject(2222);
console.log(2222);
}, 2000);
});
let promise3 = new MyPromise((resolve, reject) => {
console.log('cccc');
setTimeout(() => {
resolve(3333);
console.log(3333);
}, 3000);
});
Promise.race([promise1, promise2, promise3]).then((value) => {
console.log('all value', value);
}, (reason) => {
console.log('all reason', reason);
})
Copy the code
Print result:
aaaa
bbbb
cccc
1111
all reason 1111
2222
3333
12. To achievePromise.resolve
methods
Resolve is used to generate a fulfilled Promise, which is usually placed at the beginning of the whole Promise chain to start a Promise chain.
MyPromise.js
MyPromise.resolve = function(value) {
let promise;
promise = new MyPromise((resolve, reject) => {
this.prototype.resolvePromise(promise, value, resolve, reject);
});
return promise;
};
Copy the code
test.js
let MyPromise = require('./MyPromise.js');
MyPromise.resolve(1111).then((value) => {
console.log('value1', value);
return new MyPromise((resolve, reject) => {
resolve(2222);
})
}).then((value) => {
console.log('value2', value);
})
Copy the code
Print result:
value1 1111
value2 2222
Since the value passed in could be a normal value, thenable, or another Promise, resolvePromise is called for resolution.
12. To achievePromise.reject
methods
Reject is used to generate a Promise in the Rejected failed state.
MyPromise.js
MyPromise.reject = function(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
Copy the code
test.js
let MyPromise = require('./MyPromise.js');
MyPromise.reject(1111).then((value) => {
console.log('value1', value);
return new MyPromise((resolve, reject) => {
resolve(2222);
})
}).then((value) => {
console.log('value2', value);
}).catch(reason => {
console.log('reason', reason);
});
Copy the code
Print result:
reason 1111
13. To achievePromise.deferred
methods
Promise.deferred can be used to defer resolve and reject.
MyPromise.js
MyPromise.deferred = function() {
let dfd = {};
dfd.promies = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.rfeject = reject;
});
return dfd;
};
Copy the code
This way, you can resolve the Promise externally by calling ddfd.resolve () and ddfd.reject ().
13. How to stop onePromise
chain
Suppose we have a scenario where we have a long chain of promises that depend on each other, and if one of the promises in the chain goes wrong, we don’t have to go any further. By default, we can’t implement this requirement. Because a Promise either then or catch will return a Promise, and then or catch will continue. For example:
new Promise(function(resolve, reject) {
resolve(1111)
}).then(function(value) {
// "ERROR!!!"
}).catch()
.then()
.then()
.catch()
.then()
Copy the code
Is there any way to make this chain call in ERROR!! And then stop, and not execute all the callback functions after the chain call at all?
We wrapped a promise.stop method ourselves.
MyPromise.js
MyPromise.stop = function() {
return new Promise(function() {});
};
Copy the code
Stop returns a Promise that never executes resolve or reject. The Promise is always pending, so it never executes down then or catch. So we stop a Promise chain.
new MyPromise(function(resolve, reject) {
resolve(1111)
}).then(function(value) {
// "ERROR!!!"
MyPromise.stop();
}).catch()
.then()
.then()
.catch()
.then()
Copy the code
One disadvantage is that any callback function following the chain call cannot be collected by the garbage collector.
14. How to solve itPromise
The last one returned on the chainPromise
There is an error
Here’s an example:
new Promise(function(resolve) {
resolve(42)
}).then(function(value) {
a.b = 2;
});
Copy the code
This is a syntax error. The ondepressing callback function is wrapped in a try… In catch, the error is caught, but because there’s no “THEN” or “catch” behind it, the error can’t be handled, and it’s eaten by a Promise, without any exception, and that’s what we call a Promise that might eat an error.
So how do we deal with this?
Methods a
Done () is what we implemented earlier.
new Promise(function(resolve) {
resolve(42)
}).then(function(value) {
a.b = 2;
}).done();
Copy the code
The done() method is equivalent to a catch, but no longer returns a Promise. Note that the done() method does not have a syntax error, otherwise it will not catch.
Method 2
Normal error listening for window error events can be captured
window.addEventListener('error', error => { console.log(error); });Copy the code
A Promise that is not handled by onRejected() needs to listen for an unhandledrejection event
window.addEventListener('unhandledrejection', error => {
console.log('unhandledrejection', error); });});});Copy the code
14. Unit testing
The end of the
The unit tests and the complete code can be viewed on github. If they are helpful to you, please give them a star
Welcome to follow my public number
Reference documentation
PromiseA + specification