Written in the beginning

  • I see some technology groups are developing these daysPromiseRelated to some of the implementation, oneself recently also look atES6Some of the content, so I plan to sort out their own, improve the understanding;
  • This article is suitable for those who know and use itPromiseIf you don’t know or use itPromiseI suggest you take a look at it firstECMAScript6 Promise of getting started.

What is thePromise

  • A solution to asynchronous programming;
  • PromiseIs a container that holds the result of some event (usually an asynchronous operation) that will end in the future.

The characteristics of

  • The status of an object is not affected.PromiseThe object represents an asynchronous operation with three states:pending(In progress),fulfilled(Successfully) andrejected(Failed).
  • Once the state changes, it never changes again, and you can get this result at any time.PromiseThe state of an object can change in only two ways: frompendingintofulfilledAnd from thependingintorejected.

Simple implementation

Process analysis

  • Image credit: MDN

Initialize onePromise

  • The originalPromise
// The Promise constructor receives an executor function, and after the executor function has performed a synchronous or asynchronous operation, Resolve and reject const promise = new promise (function(resolve, reject) {/* Call resolve and pass value if the operation fails. Call reject and pass reason */})Copy the code
  • Contains the current state
  • The current value
  • fulfilledIs an array of callback functions
  • rejectedIs an array of callback functions
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; const NewPromise = function(executor) { const _this = this; _this.status = PENDING; // state _this.data = undefined; // value _this.onresolvedCallback = []; // this. OnRejectedCallback = []; Function resolve(value) {// Resolve (value) {... } function reject(reason) {... } try { executor(resolve, reject); } catch (error) { reject(error); }}Copy the code

perfectresoleandreject

  • resoleIs a successful call and needs to be changed to statelessfulfilled, then the current value is set to the value passed in, and the execution is traversedonResolvedCallbackThe callback in.
This is a pity/rejected setTimeout(function()) {// this is a pity/rejected setTimeout(function()) {// This is a pity/rejected setTimeout(function()) {// This is a pity/rejected setTimeout(function()) {// This is a pity/rejected setTimeout(function()) (_this.status === PENDING) { _this.status = FULFILLED; _this.data = value; _this.onResolvedCallback.forEach(callback => callback(value)); }}); }Copy the code
  • rejectThe implementation andresolveSimilar, except that the state is changed torejectedAnd carry outonRejectedCallback.
Function reject(reason) {setTimeout(function() {// Execute all callback functions asynchronously if (_this.status === PENDING) {_this.status = REJECTED; _this.data = reason; _this.onRejectedCallback.forEach(callback => callback(reason)); }}); }Copy the code
  • Now you have the basicsPromise, you can like usePromiseUse it as well, but no other methods have been added.
// 测试
const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11)
})
Copy the code

thenmethods

  • PromiseObject has onethenMethod used to register in thisPromiseCallback after state determination,thenMethods need to be written on the prototype chain (why are they written on the prototypeJavaScriptBasic). inPromise/AstandardIn, clearly stipulatedthenTo return a new object, we return a new object in our implementation.
/ / then mount to NewPromise on prototype. The prototype. Then = function (onResolved onRejected) {const _this = this; if ( typeof onResolved ! == 'function') { onResolved = function(value) { return value } } if ( typeof onRejected ! // Const common = function(data, resolve, resolve); // Const common = function(data, resolve, resolve); This is a big pity. // This is a big pity. This is a big pity. onResolved(data) : onRejected(data) if( value instanceof Promise) { value.then(resolve, Reject)} resolve(value)} catch (error) {reject(error)}} const pendingCommon = function (data, flag, This is a big pity. // This is a big pity. // This is a big pity. onResolved(data) : onRejected(data) if( value instanceof Promise) { value.then(resolve, reject) } resolve(value) } catch (error) { reject(error) } } if (_this.status === PENDING) { return new NewPromise(function(resolve, reject) { _this.onResolvedCallback.push((value) => { pendingCommon(value, FULFILLED, resolve, reject); }) _this.onRejectedCallback.push((reason) => { pendingCommon(reason, REJECTED, resolve, reject); }) }) } else { // resolve / reject return new NewPromise(function (resolve, reject) { setTimeout(function () { common(_this.data, resolve, reject) }) }) } }Copy the code

rightresolveMethods improved again

  • judgerosolveIs the value ofPromiseIf it isPromiseContinue to perform.thenMethods.
Function resolve(value) {then + if (value instanceof Promise) {return (value instanceof Promise) value.then(resolve, reject) + } ... }Copy the code

catchmethods

  • capturePromiseException error.
/ / catch method NewPromise. Prototype. Catch = function (onRejected) {return this. Then (null, onRejected)}Copy the code

finallymethods

  • finally()Method is used to specify whetherPromiseThe operation to be performed in the final state of the object.
// finally
NewPromise.prototype.finally = function (fun) {
  return this.then((value) => {
      return NewPromise.resolve(fun()).then(() => value);
  }, (err) => {
      return NewPromise.resolve(fun()).then(() => {
          throw err;
      });
  });
};
Copy the code

resolvemethods

  • Existing object convertedPromiseObject, instance of which is in the state offulfilled.
NewPromise.resolve = function(value) { if (value instanceof Promise) return value; if (value === null) return null; / / to judge if it is a promise if (typeof value = = = 'object' | | typeof value = = = 'function') {try {/ / judgment method are then let then = value.then; if (typeof then === 'function') { return new NewPromise(then.call(value)); Catch (e) {return new NewPromise((resolve, reject) =>{reject(e); }); } } return new NewPromise( (resolve, reject) =>{ resolve(value); }); }Copy the code

rejectmethods

  • Existing object convertedPromiseObject, instance of which is in the state ofrejected. The implementation andresolveThe method is similar, and finally return is changedreject.
NewPromise.reject = function(reason) {
    ...
    return new NewPromise( (resolve, reject) =>{
      reject(reason);
  });
}
Copy the code

allmethods

  • Promise.allMethod is used to wrap multiple Promise instances into a new onePromiseInstance; The arguments to a method may not be arrays, but they must beIteratorInterface, and each member returned isPromiseInstance.
// all NewPromise.all = function(promises) { return new NewPromise((resolve, Reject) => {const result = [] // Promise = array.isarray (promises)? promises : [] const len = promises.length; if(len === 0) resolve([]); let count = 0 for (let i = 0; i < len; ++i) { if(promises[i] instanceof Promise) { promises[i].then(value => { count ++ result[i] = value if (count === len) resolve(result) }, reject) } else { count ++ result[i] = promises[i] if (count === len) resolve(result) } } }) }Copy the code

racemethods

  • Promise.race()The method is also multiplePromiseInstance, wrapped into a new onePromiseInstance.
  • As long as one instance of the parameters changes state first,Promise.race()The state of the changes. The one who changed firstPromiseThe return value of the instance is passed toPromise.race()The callback function of.
// race
NewPromise.race = function (promises) {
  return new NewPromise((resolve, reject) => {
    promises = Array.isArray(promises) ? promises : []
    promises.forEach(promise => {
      promise.then(resolve, reject)
    })
  })
}
Copy the code

deferredmethods

  • Returns an object with a Promise method.
// defered
NewPromise.deferred = function() {
  const dfd = {}
  dfd.promise = new NewPromise(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
Copy the code

allSettledmethods

  • Promise.allSettled()Method accepts a set ofPromiseThe instance is wrapped as a new parameterPromiseThis will be fulfilled only after all these parameter instances return the result, whether fulfilled or Rejected. Returns an array containing all the results.
NewPromise.allSettled = function (promises) { return new NewPromise((resolve, reject) => { promises = Array.isArray(promises) ? promises : [] let len = promises.length; const argslen = len; if (len === 0) return resolve([]); let args = Array.prototype.slice.call(promises); Function resolvePromise(index, value) {if(typeof value === 'object') {const then = value.then; if(typeof then === 'function'){ then.call(value, function(val) { args[index] = { status: 'fulfilled', value: val}; if(--len === 0) { resolve(args); } }, function(e) { args[index] = { status: 'rejected', reason: e }; if(--len === 0) { reject(args); } }) } } } for(let i = 0; i < argslen; i++){ resolvePromise(i, args[i]); }})}Copy the code

test

Based on the test

// test const promise = new NewPromise(function(resolve, reject) {console.log('ss', 11); resolve(123); })Copy the code

thentest

promise.then(val => {
  console.log('val', val)
})
Copy the code

catchtest

const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11);
  reject('errr')
})
promise.catch(err => {
  console.log('err', err)
})
Copy the code

finallytest

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
}).catch(err => {
  console.log('err', err)
}).finally(() => {
  console.log('finally')
})
Copy the code

resolvetest

const resolved = NewPromise.resolve(1);
resolved.then(val => {
    console.log('resolved', val)
})
Copy the code

rejecttest

const rejected = NewPromise.reject(-1);
rejected.catch(val => {
  console.log('rejected', val)
})
Copy the code

alltest

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
}).catch(err => {
  console.log('err', err)
})
Copy the code

allSettledtest

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.allSettled([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
})
Copy the code

All the code

  • Please click to jumpGitHubWarehouse:Handwritten Promise implementation, wiggle your finger and give the project oneStarThank you!

reference

ECMAScript 6 introduction to 22 high-frequency JavaScript handwritten interview questions and answers

  • Have to help you leave your praise 👍 bai, your support is the power of my output.

supplement

rightPromise.allThere is doubt

  • Tested nativePromise.allMethods.

  • When all the pity

Change the asynchronous success return sequence of the all method

  • The code for the all method above has been updated.
  • Test cases, thanks to AshoneA for asking the question.
const promise1 = new Promise(resolve => {
  setTimeout(() => {
    resolve("promise1");
  }, 200);
});
const promise2 = new Promise(resolve => {
  setTimeout(() => {
    resolve("promise2");
  }, 100);
});
NewPromise.all([promise1, promise2]).then(value => {
  console.log(value);
});

Promise.all([promise1, promise2]).then(value => {
  console.log(value);
});
Copy the code
  • Previous test results. [Test Address](

Codesandbox. IO/s/musing – cu…)

  • Modified test results.