如何手写一个Promise(一)

280 阅读3分钟

前言

这里引用阮老师ES6入门里对Primose对象的定义

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。 它有两个特点:

  1. 对象的状态不受外界影响
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果

实现Promise

基于概念,接下来手写一个Promise

class PromiseA {
  constructor(exectuor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    const resolve = (value) => {
      if (this.status === 'pending') {
        this.value = value;
        this.status = 'fulfilled';
      }
    }
    const reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status === 'rejected';
      }
    }
    exectuor(resolve, reject);
  }

  then(onResolved, onRejected) {
    const resolved = typeof onResolved === 'function' ? onResolved : v => v;
    const rejected = typeof onRejected === 'function' ? onRejected : r => r;
    if (this.status === 'fulfilled') {
      resolved(this.value);
    }
    if (this.status === 'rejected') {
      rejected(this.reason);
    }
  }
}

代码很简单,重点是在resolve,reject两个方法中改变当前Promise的状态。 测试一下,一切顺利

const promise = new PromiseA((resolve) => {
  resolve('promise success');
}).then((data) => {
  console.log(data); // promise success
})

如果在Promise的回调函数中异步处理resolve,最终并没有得到想要的结果,原因在于此时调用then,status还处于pending状态,根本就不会执行then方法中的resolved或者rejected。

new PromiseA((resolve, reject) => {
  setTimeout(() => {
    resolve('promise success')
  });
}).then((data) => {
	// 注意:根本不会执行到这里
  console.log(data);
})

这样需要增加对异步调用的处理,基本思路是先将resolved或者rejected方法加到数组中,等到resolve()执行时,遍历数组中的resolved/rejected,其实本质就是观察者模式。

class PromiseA {
  constructor(exectuor) {
   
	  ...... // 省略部分
    
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.status === 'pending') {
        this.value = value;
        this.status = 'fulfilled';
        // 循环遍历执行resolved()
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    }
    const reject = (reason) => {
      if (this.status === 'pending') {
        this.reason = reason;
        this.status === 'rejected';
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }
    exectuor(resolve, reject);
  }

  then(onResolved, onRejected) {
    
    ...... // 省略部分

	  // 新增部分
    if(this.status === 'pending') {
      this.onResolvedCallbacks.push(() => {
        resolved(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        rejected(this.reason);
      })
    }
  }
}

测试一下,一切顺利。

const promise = new PromiseA((resolve) => {
  setTimeout(() => {
    resolve('promise success');
  });
}).then((data) => {
  console.log(data); // promise success
})

then的链式调用

采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。 要实现then的链式调用,必须对then的返回值进行处理,增加一个方法用于处理then方法的返回值。

// 处理then的链式调用
function resolveThen(promisable, promise, resolve, reject) {
  if (promisable === promise) {
    throw ('promise不能为同一个引用对象');
  }
  try {
    // 判断promise是否为对象,如果是做进一步处理
    // 如果不是,说明promise是基本值,直接用resolve处理
    if (promise && typeof promise === 'object') {
      const then = promise.then;
      // 判断promise是否有then方法,如果有做进一步处理
      // 如果没有,说明promise是个普通对象,直接用resolve处理
      if (then && typeof then === 'function') {
        // 递归处理是promise的情况
        then.call(promise, y => {
          resolveThen(promise2, y, resolve, reject);
        }, r => {
          reject(r);
        })
      } else {
        resolve(promise);
      }
    } else {
      resolve(promise)
    }
  } catch (error) {
    reject(error);
  }
}

then方法返回一个Promise对象

export class PromiseA {
  
  ...... // 省略

  then(onResolved, onRejected) {
    const resolved = typeof onResolved === 'function' ? onResolved : v => v;
    const rejected = typeof onRejected === 'function' ? onRejected : r => r;

    const promise = new PromiseA((resolve, reject) => {
      if (this.status === 'fulfilled') {
        const value = resolved(this.value);
        resolveThen(promise, value, resolve, reject);
      }
      if (this.status === 'rejected') {
        const reason = rejected(this.reason);
        resolveThen(promise, reason, resolve, reject);
      }
      if (this.status === 'pending') {
        this.onResolvedCallbacks.push(() => {
          const value = resolved(this.value);
          resolveThen(promise, value, resolve, reject);
        });
        this.onRejectedCallbacks.push(() => {
          const reason = rejected(this.reason);
          resolveThen(promise, reason, resolve, reject);
        })
      }
    });
    return promise;
  }
}

这样就实现了then的链式调用,测试一下,一切顺利

const promise = new PromiseA((resolve) => {
  setTimeout(() => {
    resolve('promise success');
  });
}).then((data) => {
  console.log(data); // promise success
  return 'promise then success';
}).then((data) => {
  console.log(data); // promise then success
})

后续会更新Promise.resolve,Promise.all等静态方法的实现。