likes
comments
collection
share

Promise用了这么久,还不来看看怎么简单的手撕嘛?

作者站长头像
站长
· 阅读数 48

基本使用

我们先要了解几个基础知识:

  • Promise实例对象内部会有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
  • 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

基本使用:

 let p1 = new Promise((resolve, reject) => {
   resolve('成功');
   reject('失败');
   throw('报错');  //相当于reject()
 })

 console.log(p1);
 
 let p2 = new Promise((resolve, reject) => {
     resolve('1')
 }).then((val) => {
     console.log(val);
 })
 
// Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

让我们看看打印的结果分别是什么吧 :

Promise用了这么久,还不来看看怎么简单的手撕嘛?

Promise用了这么久,还不来看看怎么简单的手撕嘛?

Promise用了这么久,还不来看看怎么简单的手撕嘛?

Promise用了这么久,还不来看看怎么简单的手撕嘛?

实现简单Promise

定义Promise类

class myPromise {
  constructor(executor) {
    this.status = 'pending'; // 变更promise的状态
    this.value = null;
    
    executor(this.resolve, this.reject); // new 一个 myPromise 得到的实例对象里面有两个函数
  }

  resolve(value) {
    if (this.status !== 'pending') return;
    this.status = 'fulfilled'; // 变更 promise的状态
    this.value = value;
  }

  reject(reason) {
    if (this.status !== 'pending') return
    this.status = 'rejected';
    this.value = reason;
  }
}

那么让我们看一下实现的效果:

Promise用了这么久,还不来看看怎么简单的手撕嘛?

很明显,我们reslove和reject的指向出了问题,内部指向了构造器,而非整个类,因此考虑使用bind改变指向。

executor(this.resolve.bind(this), this.reject.bind(this))

Promise用了这么久,还不来看看怎么简单的手撕嘛?

并且myPromise的状态一经变更也不再改变,是不是有一点原装Promise的味道了。但是在Promise里面还有一个错误捕捉机制,只要promise里面执行的逻辑报错了,就需要走reject逻辑,将错误抛出来,那我们只需要使用try catch来实现就可以。

try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error)
    }

这样我们就实现了极简版的Promise对象。

class myPromise {
  constructor(executor) {
    this.status = 'pending'; 
    this.value = null;
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error)
    }
  }

  resolve(value) {
    if (this.status !== 'pending') return;
    this.status = 'fulfilled';
    this.value = value;
  }

  reject(reason) {
    if (this.status !== 'pending') return
    this.status = 'rejected';
    this.value = reason;
  }
}

实现.then方法

我们需要完成下面几件事:

  • 一个promise可以有多个then,因此我们需要通过数组的形式来存放其中的回调
// 多个then的情况
let p = new Promise();
p.then();
p.then();
  • 需要实现将promise返回的值,传递到.then()
  • 当我们在第一个then中return了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值
  • 不可以在.then中返回自身
let p = new Promise(resolve => {
  resolve(0);
});
var p2 = p.then(data => {
  // 循环引用,自己等待自己完成,一辈子完不成
  return p2;
})
  • 如果回调返回的是promise对象,则取它的结果

实现如下:

class myPromise {
  constructor(executor) {
    this.status = 'pending'; 
    this.value = null;
    this.onFulfilledCallbacks = []; // 用来保存成功的回调(处理异步)
    this.onRejectedCallbacks = []; // 用来保存失败的回调(处理异步)
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error)
    }
  }

  resolve(value) {
    if (this.status !== 'pending') return;
    this.status = 'fulfilled';
    this.value = value;
    // 调用then里面的回调
    while (this.onFulfilledCallbacks.length) { // 当异步成功回调数组中存在回调函数,那就执行
      this.onFulfilledCallbacks.shift()(this.value)
    }
  }

  reject(reason) {
    if (this.status !== 'pending') return
    this.status = 'rejected';
    this.value = reason;
    while (this.onRejectedCallbacks.length) { // 当异步失败回调数组中存在回调函数,那就执行
      this.onRejectedCallbacks.shift()(this.value)
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val  // 判断.then的第一个参数是不是一个函数,如果不是就直接作为结果返回
    onRejected = typeof onRejected === 'function' ? onRejected : val => { throw val } // 判断.then的第二个参数是不是一个函数,如果不是就直接作为错误返回

    var thenPromise = new myPromise((resolve, reject) => {  // 因为.then返回的是一个心的Promise对象

      const resolvePromise = callback => {  // 用于判断回调函数的类型
        setTimeout(() => {   // 让整个回调函数比同步代码晚一点执行,官方不是使用setTimeout实现
          try {
            const x = callback(this.value);
            if (x === thenPromise) {  // 你正在返回自身
              throw new Error('不允许返回自身!');
            }
            if (x instanceof myPromise) { // 返回的是一个Promise对象时
              x.then(resolve, reject);
            } else { // 直接返回一个值,作为resolve的值,传递给下一个.then
              resolve(x);
            }
          } catch (error) {
            reject(error);
            throw new Error(error)
          }
        })
      }

      if (this.status === 'fulfilled') {
        resolvePromise(onFulfilled)
      } else if (this.status === 'rejected') {
        resolvePromise(onRejected)
      } else if (this.status === 'pending') {// 当resolve在setTomeout内执行,状态不是马上改变
        this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled));// 绑定与包装
        this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected));
      }
    })
    return thenPromise
  }
}

于是我们可以看看下面的题:

       const promise = new Promise((resolve, reject) =>{
            console.log('1');
            resolve(5);
            console.log('2');
            reject(6);
        }).then((val) => {
            console.log(val);
        }).catch((err) => {
            console.log(err);
        })
        promise.then(() => {
            console.log('3');
        })
        console.log('4');
        setTimeout(() => {
            console.log('7');
        })
        // 1 2 4 5 3 7
  • resolve()执行之后,不是return了,只是开始让微任务开启了
  • setTimeout()为什么在最后执行,因为这个微任务要执行完才到宏任务,并不是说.then()之后先会执行之前剩下宏任务

参考:

Promise 对象 - ECMAScript 6入门 (ruanyifeng.com)

转载自:https://juejin.cn/post/7269710614071066680
评论
请登录