As the advanced version of asynchronous programming scheme callback, Promise solves the problem of callback callback region and adds some features: for example, all and race methods make Promise more powerful and flexible in dealing with asynchronous programming.
Promise features
- The constructor needs a parameter executor function. Executor takes both resolve and reject as arguments. Resolve is the function that will be called when a promise is translated into a fulfilled state. Reject is the function called when you convert a promise to rejected (a failed state).
- Promise has three states: pity, fulfilled, and rejected. This state can only be fulfilled by pending– >fulfilled or pending– > Fulfilled. This is a pity and rejected
- The promise’s then function takes onFullfilled and onRejected callback functions. These two callback functions will be called after the promise confirms the state
- The onFullfilled and onRejected callback functions are added to the microtask queue.
- If there is a setTimeout delay in the executor, then the promise is temporarily pending, When executing the then function, two callback queues, onFullfilledCallbacks and onRejectedCallbacks, are required to cache the corresponding callback functions respectively
- The then function returns a new promise as the next chain call
According to the above features, a preliminary implementation of an initial promise is as follows:
class Promise {
constructor(executor) {
this.status = 'pending'// Default state this.value = undefined; // Save the argument this.reason = undefined; / / failed to save the reason and reject this. OnFullfilledCallbacks = []; This.onrejectedcallbacks = []; // Callback queue on failurelet resolve = (data) => {
this.status = 'fullfilled';
this.value = data;
this.onFullfilledCallbacks.forEach(fn => fn());
}
let reject = (err) => {
this.status = 'rejected'; this.reason = err; this.onRejectedCallbacks.forEach(fn => fn()); } try { executor(resolve, reject); } Catch (err) {// If an error is reported, the actuator changes to a failed state and transmits the failure cause to reject(err). }}then(onFullfilled, onRejected) {
letpromise2; // Return a new promiseif(this.status === 'fullfilled'Promise2 = new Promise((resolve, reject) => {setTimeout(() => {
try {
letx = onFullfilled(this.value); resolve(x); } catch (err) {// Fail sends the error reject(err); }}, 10); }); }if(this.status === 'rejected'Promise2 = new Promise((resolve, reject) => {setTimeout(() => {
try {
letx = onRejected(this.reason); resolve(x); // Even if you fail next timethen} catch (err) {// Fail sends the error out reject(err); }}, 10); }); }if(this.status === 'pending') {/ / pending state needs to be corresponding callback cached promise2 = new Promise ((resolve, reject) = > {this. OnFullfilledCallbacks. Push (() = > {setTimeout(() => {
try {
letx = onFullfilled(this.value); resolve(x); } catch (err) {// Fail sends the error reject(err); }}, 10); }); this.onRejectedCallbacks.push(() => {setTimeout(() => {
try {
letx = onRejected(this.reason); resolve(x); // Even if you fail next timethen} catch (err) {// Fail sends the error out reject(err); }}, 10); }); }); }returnpromise2; }}Copy the code
Then chain call implementation
According to the Promise A+ specification, the return value x requires the following parsing:
X is equal to promise
If a promise and x point to the same object, reject the promise as TypeError grounds
2. X for the Promise
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
3. 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
4. If x is not an object or function
Execute the promise with an x argument
ResolvePromise code implementation
function resolvePromise(promise2, x, resolve, reject) {
let called = false;
if(promise2 === x) {// x equals promisereturn reject(new TypeError('Circular reference')); // Throw an error}if(x instanceof Promise) {
if(x.status === 'pending') {// if x is in the wait state.function(value) { resolvePromise(promise2, value, resolve, reject); // Promise should remain in a wait state until x is implemented or rejected. }else{// if x is in the execute or reject state x.tohen (resolve, reject); }return;
}
if(x ! == null && (typeof x ==='object' || typeof x === 'function'//x is an object or a function try {let then = x.then;
if(typeof then= = ='function'If) {/ /thenIs the function then. Call (x,function(y) {//resolvePromise is called with the value yif(called) {// If both resolvePromise and rejectPromise are called, or if the same parameter is called more than oncereturn; // the first call is preferred and the remaining calls are ignoredtrue; resolvePromise(promise2, y, resolve, reject); Run [[Resolve]](promise, y)},function(r) {// If rejectPromiseif(called) {// If both resolvePromise and rejectPromise are called, or if the same parameter is called more than oncereturn; // the first call is preferred and the remaining calls are ignoredtrue; reject(r); // reject a promise for r}); }else{/ / ifthenNot the function resolve(x); // Execute promise}} catch (err) {// Throw an error if x.teng is usedif(called) {// If resolvePromise or rejectPromise has been calledreturn; // Ignore} called =true; reject(err); // Reject a promise for e}}else{// Resolve (x) if x is not an object; // Make a promise with x}}Copy the code
Other supplementary
1. The then chain call of a Promise has another feature, that is, it can pass data in sequence when no parameters are passed in the intermediate THEN, for example:
Promise.resolve('aaaa').then().then().then(function(data) {console.log(data)});
This is because onFullfilled and onRejected are given default values if they are not passed in, like this:
if(typeof onFullfilled ! = ='function') {
onFullfilled = value => value;
}
if(typeof onRejected ! = ='function') { onRejected = err => { throw err; }}Copy the code
2. The catch method of the Promise instance is similar to the then method without passing onFullfilled parameter, which is implemented as follows:
catch(callback) {
return this.then(null, callback);
}
Copy the code
3. The static ALL method of a Promise can return or data after all incoming promises succeed, and all of them fail if one of them fails.
static all(promises) {
let dataArr = [];
let len = promises.length;
letcount = 0; // The number of successful statisticsreturn new Promise(function(resolve, reject) {
for (leti = 0; i < len; i++) { p.then(function(data) {
dataArr[i] = data;
if(++count === len) {// Resolve (dataArr); } }, reject); }}); }Copy the code
4. The Promise’s static race method causes the first successful result in the incoming Promise to be returned, with one successful result
static race(promises) {
let len = promises.length;
return new Promise(function(resolve, reject) {
for (leti = 0; i < promises.length; i++) { promises[i].then(resolve, reject); }}); }Copy the code
Full Promise implementation
class Promise {
constructor(executor) {
this.status = 'pending'// Default state this.value = undefined; // Save the argument this.reason = undefined; / / failed to save the reason and reject this. OnFullfilledCallbacks = []; This.onrejectedcallbacks = []; // Callback queue on failurelet resolve = (data) => {
this.status = 'fullfilled';
this.value = data;
this.onFullfilledCallbacks.forEach(fn => fn());
}
let reject = (err) => {
this.status = 'rejected'; this.reason = err; this.onRejectedCallbacks.forEach(fn => fn()); } try { executor(resolve, reject); } Catch (err) {// If an error is reported, the actuator changes to a failed state and transmits the failure cause to reject(err). }}then(onFullfilled, onRejected) {
letpromise2; // Return a new promiseif(typeof onFullfilled ! = ='function') {
onFullfilled = value => value;
}
if(typeof onRejected ! = ='function') { onRejected = err => { throw err; }}if(this.status === 'fullfilled'Promise2 = new Promise((resolve, reject) => {setTimeout(() => {
try {
letx = onFullfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (err) {// Fail sends the error reject(err); }}, 10); }); }if(this.status === 'rejected'Promise2 = new Promise((resolve, reject) => {setTimeout(() => {
try {
letx = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); // Even if you fail next timethen} catch (err) {// Fail sends the error out reject(err); }}, 10); }); }if(this.status === 'pending') {/ / pending state needs to be corresponding callback cached promise2 = new Promise ((resolve, reject) = > {this. OnFullfilledCallbacks. Push (() = > {setTimeout(() => {
try {
letx = onFullfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (err) {// Fail sends the error reject(err); }}, 10); }); this.onRejectedCallbacks.push(() => {setTimeout(() => {
try {
letx = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); // Even if you fail next timethen} catch (err) {// Fail sends the error out reject(err); }}, 10); }); }); }return promise2;
}
catch(callback) {
return this.then(null, callback);
}
static all(promises) {
let dataArr = [];
let len = promises.length;
letcount = 0; // The number of successful statisticsreturn new Promise(function(resolve, reject) {
for (leti = 0; i < len; i++) { p.then(function(data) {
dataArr[i] = data;
if(++count === len) {// Resolve (dataArr); } }, reject); }}); } static race(promises) {let len = promises.length;
return new Promise(function(resolve, reject) {
for (leti = 0; i < promises.length; i++) { promises[i].then(resolve, reject); }}); } staticdeferred() {
let dfd = {};
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve;
dfd.reject = reject;
});
returndfd; }}function resolvePromise(promise2, x, resolve, reject) {
let called = false;
if(promise2 === x) {// x equals promisereturn reject(new TypeError('Circular reference')); // Throw an error}if(x instanceof Promise) {
if(x.status === 'pending') {// if x is in the wait state.function(value) { resolvePromise(promise2, value, resolve, reject); // Promise should remain in a wait state until x is implemented or rejected. }else{// if x is in the execute or reject state x.tohen (resolve, reject); }return;
}
if(x ! == null && (typeof x ==='object' || typeof x === 'function'//x is an object or a function try {let then = x.then;
if(typeof then= = ='function'If) {/ /thenIs the function then. Call (x,function(y) {//resolvePromise is called with the value yif(called) {// If both resolvePromise and rejectPromise are called, or if the same parameter is called more than oncereturn; // the first call is preferred and the remaining calls are ignoredtrue; resolvePromise(promise2, y, resolve, reject); Run [[Resolve]](promise, y)},function(r) {// If rejectPromiseif(called) {// If both resolvePromise and rejectPromise are called, or if the same parameter is called more than oncereturn; // the first call is preferred and the remaining calls are ignoredtrue; reject(r); // reject a promise for r}); }else{/ / ifthenNot the function resolve(x); // Execute promise}} catch (err) {// Throw an error if x.teng is usedif(called) {// If resolvePromise or rejectPromise has been calledreturn; // Ignore} called =true; reject(err); // Reject a promise for e}}else{// Resolve (x) if x is not an object; // Make a promise with x}}Copy the code