Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.


One, foreword

In the previous article, I translated and understood the whole Promise A+ specification;

At the beginning of this article, will be based on the Promise A+ specification, on our simple version of the Promise source code for functional improvement;

This article will implement Promise’s support for asynchronous operations;


Two, the previous review

In the first few articles, combined with the use of the Promise basic function introduction and simple analysis, handwritten a simple version of the Promise source code, mainly covering the following points:

  • Promise is a class;
  • When a promise is used, the executor executor is passed in and executed immediately;
  • The Executor parameters are two functions that describe the state of a Promise instance;
  • Resolve indicates success, and a value can be passed.
  • Reject indicates failure. You can pass in a reason.
  • Each Promise instance has a THEN method;
  • Once a promise state has occurred, it cannot be changed.
  • Promise has three states: a success state, a failure state, and a wait state (default).

Brief version of the source code is as follows:

const PENDING = 'PENDING';     / / wait state
const DULFILLED = 'DULFILLED'; / / success
const REJECTED = 'REJECTED';   / / failure mode

class Promise{
  // The executor executor function is passed in when instantiated through a new Promise
  constructor(executor){

    this.value = undefined; // Save the reason for success, which will be passed in when ondepressing is called in then
    this.reason = undefined; // Save the reason for the failure, which will be passed in when ondepressing is called in then
    this.state = PENDING;   // Promise state, default wait state
    
    // Reslove succeeds, reject fails, and executor is passed
    const reslove = (value) = >{
      // Wait state --> Success state
      if(this.state === PENDING){
        this.value = value
        this.state = DULFILLED; }}const reject = (reason) = >{
      // Wait state --> failed state
      if(this.state === PENDING){
        this.reason = reason
        this.state = REJECTED; }}// Execute the executor executor function immediately, using the try... catch... To catch exceptions;
    try{
      executor(reslove, reject);
    }catch(e){
      reject(e)  Reject (reject) reject (reject}}// Define the then method: onFulfilled and onRejected;
  // According to the Promise state, perform onFulfilled or onRejected;
  then(onFulfilled, onRejected){
    // Call ondepressing and pass in success value
    if(this.state === DULFILLED){
      onFulfilled(this.value)
    } 
    // In failed state, execute onRejected and pass in failed reason
    if(this.state === REJECTED){
      onRejected(this.reason)
    }
  }
}

module.exports = Promise; 
Copy the code

Third, ask questions

One of the main uses of Promise is to handle asynchronous operations, allowing developers to use synchronous programming to handle asynchronous operations, which can better solve/avoid callback hell than the previous callback method, and at the same time reverse control.

1. Test the native Promise

Perform asynchronous operations using native Promises:

let promise = new Promise((resolve, reject) = >{
  setTimeout(() = >{
    resolve('ok');
  }, 1000)
})

promise.then((value) = >{
  console.log('success', value) 
},(reson) = >{
  console.log('err',reson)
})

// success ok
Copy the code

2. Test handwritten promises

// Introduce handwritten promise.js
let promise = new Promise((resolve, reject) = >{
  setTimeout(() = >{
    resolve('ok');
  }, 1000)
})

promise.then((value) = >{
  console.log('success', value) 
},(reson) = >{
  console.log('err',reson)
})
Copy the code

The implementation result will not output any content. That is, the successful callback onFulfilled and the failure callback onRejected in then are not executed.

3. Problem analysis

Analysis of execution process:

  • When the new Promise completes, the executor executor function is immediately executed and the timer is started.
  • Promise. then is executed. The current PROMISE instance state is pending and no operation is performed.
  • 1 second later, the definer callback executes, calling resolve to update the current PROMISE instance state to a successful state;

Cause of the problem:

  • Reslove is called after the timer is 1 second, and then has been executed.
  • Therefore, the current promise source code does not support asynchronous operations, need to achieve support for asynchronous operations;

4. Solutions

  • Use queues to collect callbacks from the then method first.
  • When the resolve or REJECT status is updated, the corresponding callback function can be executed.

This “collect first, execute later” function is in line with the subscription publishing mode;


Implement Promise’s support for asynchronous operations

1. Event subscription: Collect callback functions

  • When a Promise performs an asynchronous operation, the state is PENDING;
  • At this point, two queues (arrays) are required to collect the success/failure callback separately;
class Promise{
  constructor(executor){
    this.state = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = []; // Collection success callback
    this.onRejectedCallbacks = []; // Collect failed callback
    // ...
  }

  then(onFulfilled, onRejected){
    // Collect success/failure callback when performing asynchronous operation
    if(this.state === PENDING){
      this.onResolvedCallbacks.push(() = >{
        onFulfilled(this.value) / / the incoming value
      })
      this.onRejectedCallbacks.push(() = >{
        onRejected(this.value) / / the incoming value})}// ...}}Copy the code

This completes the event subscription operation for the success/failure callback function;

2. Event publishing: Perform callback processing based on state

When the resolve or reject functions in a Promise are called, the corresponding callback processing is performed based on the state:

class Promise{
  constructor(executor){
    this.state = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const reslove = (value) = >{
      if(this.state === PENDING){
        this.value = value
        this.state = DULFILLED;
        // Event publish operation
        this.onResolvedCallbacks.forEach(fn= >fn())
      }
    }
    
    const reject = (reason) = >{
      if(this.state === PENDING){
        this.reason = reason
        this.state = REJECTED;
        // Event publish operation
        this.onRejectedCallbacks.forEach(fn= >fn())
      }
    }
    // ...
  }
  // ...
}
Copy the code

This is equivalent to event publishing in subscription publishing mode;

3. Test support for asynchronous operations

let promise = new Promise((resolve, reject) = >{
  setTimeout(() = >{
    resolve('ok');
  }, 1000)
})

promise.then((value) = >{
  console.log('success', value) 
},(reson) = >{
  console.log('err',reson)
})

// success ok
Copy the code

Five, the end

This article mainly implemented Promise’s support for asynchronous operations, mainly involving the following points:

  • Test Promise’s support for asynchronous operations;
  • Analyze current Promise code issues and solutions;
  • Use publish and subscribe idea to support asynchronous operation.
  • Promise tests for asynchronous operations;

Next, chain calls that implement promises;