1

js-手撕8

 1 year ago
source link: https://www.clzczh.top/2022/12/24/js-%E6%89%8B%E6%92%958/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

JS手撕(八) Promise

Promise实现

Promise的原理之前有写过两篇博客,就不细讲了。

但还是需要简单复习一下下。

Promise构造函数的实现

promise的状态一开始是pending,只能从pending变为resolved或从pending变为rejected。并且是不可逆的。

改变promise的状态有三种方法,

  • 调用 resolve 函数:pending => fulfilled(resolved)
  • 调用 reject 函数:pending => rejected
  • 抛出异常:pending => rejected
// 1. 初始状态:pending
const p1 = new Promise((resolve, reject) => { });

// 2. 调用resolve函数:pending => fulfilled(resolved)
const p2 = new Promise((resolve, reject) => resolve(123));

// 3. 调用reject函数:pending => rejected
const p3 = new Promise((resolve, reject) => reject('error'));

// 4. 抛出异常:pending => rejected
const p4 = new Promise((resolve, reject) => {
  throw new Error('异常');
});


console.log(p1);
console.log(p2);
console.log(p3);
console.log(p4);

从上图可以看出,promise对象还会有PromiseStatePromiseResult属性。

  • PromiseState:对象的状态

  • PromiseResult:对象结果值

class MyPromise {
  // Promise对象的状态
  PromiseState = 'pending';

  // Promise对象的结果
  PromiseResult = undefined;

  constructor(executor) { // 执行器函数executor

    // 需要有`resolve`和`reject`函数,因为实例化Promise对象时需要传参`(resolve, reject) => { }`形式的函数
    const resolve = (data) => {
      if (this.PromiseState === 'pending') {
        // 只能从`pending`状态变为`resolved`
        this.PromiseState = 'fulfilled';
        this.PromiseResult = data;
      }
    };

    const reject = (data) => {
      if (this.PromiseState === 'pending') {
        // 只能从`pending`状态变为`rejected`
        this.PromiseState = 'rejected';
        this.PromiseResult = data;
      }
    };

    executor(resolve, reject);
  }
}

前3种情况已经能够实现了

// 1. 初始状态:pending
const p1 = new MyPromise((resolve, reject) => { });

// 2. 调用resolve函数:pending => fulfilled(resolved)
const p2 = new MyPromise((resolve, reject) => resolve(123));

// 3. 调用reject函数:pending => rejected
const p3 = new MyPromise((resolve, reject) => reject('error'));

console.log(p1);
console.log(p2);
console.log(p3);

但是,抛出异常的情况还不能实现,因为异常没有被捕获,所以会直接报错,后面的代码也不能执行。所以要我们在调用执行器函数executor时,应该添加try catch,如果被捕获,那就要执行reject方法。

try {
  executor(resolve, reject);
} catch (err) {
  reject(err);
}

then方法的实现

首先,简单的实现一下

then(onResolved, onRejected) {
  if (this.PromiseState === "fulfilled") {
    onResolved(this.PromiseResult);
  } else if (this.PromiseState === "rejected") {
    onRejected(this.PromiseResult);
  }
}
// 1. 初始状态:pending
const p1 = new MyPromise((resolve, reject) => { });

// 2. 调用resolve函数:pending => fulfilled(resolved)
const p2 = new MyPromise((resolve, reject) => resolve(123));

// 3. 调用reject函数:pending => rejected
const p3 = new MyPromise((resolve, reject) => reject('error'));

// 4. 抛出异常:pending => rejected
const p4 = new MyPromise((resolve, reject) => {
  throw new Error('异常');
});


p1.then((data) => {
  console.log(data);
});

p2.then((data) => {
  console.log(data);
});

p3.then(null, (reason) => {
  console.error(reason);
});

p4.then(null, (reason) => {
  console.error(reason);
});

完美(×),使用Promise难免遇到异步任务,所以还需要测试一下。

const p5 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('123456');
  }, 0)
})

p5.then((data) => {
  console.log(data);
})

没反应。这是因为因为是异步任务,所以执行then方法时,状态还是pending,所以还需要定义数据结构来存回调函数,如果是异步任务,即状态是pending时,存好回调函数。

then(onResolved, onRejected) {
  if (this.PromiseState === "fulfilled") {
    onResolved(this.PromiseResult);
  } else if (this.PromiseState === "rejected") {
    onRejected(this.PromiseResult);
  } else if (this.PromiseState === 'pending') {
    this.resolvedQueue.push(onResolved);
    this.rejectedQueue.push(onRejected);
  }
}

存好回调函数,自然还是需要调用的。所以resolve函数和reject函数最后还需要遍历执行对应回调队列。

const resolve = (data) => {
  if (this.PromiseState === 'pending') {
    // 只能从`pending`状态变为`resolved`
    this.PromiseState = 'fulfilled';
    this.PromiseResult = data;

    this.resolvedQueue.forEach(callback => callback(data));
  }
};

reject函数同理

这样子就能处理异步任务了。

但是,这个时候Promise.then()方法并不是异步任务。

const p5 = new MyPromise((resolve, reject) => {
  resolve('123456');
  console.log(88888);
})

p5.then((res) => {
  console.log(res)
})

console.log(99999);

根据JavaScript的执行机制,应该先执行完同步任务,再执行异步任务,并且Promise.then()是异步任务(微任务),所以应该依次输出8888899999123456才对。

所以,then方法还得改一下,需要添加定时器来让它变成异步任务。

then(onResolved, onRejected) {
  if (this.PromiseState === "fulfilled") {
    setTimeout(() => {
      onResolved(this.PromiseResult);
    })

  } else if (this.PromiseState === "rejected") {
    setTimeout(() => {
      onRejected(this.PromiseResult);
    })

  } else if (this.PromiseState === 'pending') {
    this.resolvedQueue.push(onResolved);
    this.rejectedQueue.push(onRejected);
  }
}

小优化:将then中的参数变为可选。实际上原生Promise.then()方法的参数不传参数或者只传一个参数都不会影响执行。

原理很简单,只需要在then方法最前面判断参数是不是函数,不是则把它变成函数即可。

if (typeof onRejected !== "function") {
  onRejected = (reason) => {
    throw reason;
  };
}

if (typeof onResolved !== "function") {
  onResolved = (value) => value;
}

then()方法返回结果,实现链式调用。原理就是返回一个新的Promise对象。当然不能只是返回一个Promise对象就行了,还需要判断回调得到的结果是不是Promise对象,如果是,还得调用then()方法,将状态变为fulfilled 或者rejected,并变更结果为then()方法返回的值。

const p5 = new Promise((resolve, reject) => {
  resolve('123456');
})

const p6 = p5.then(() => {
  return 123567;
});

const p7 = p6.then(() => {
  return new Promise((resolve, reject) => {
    resolve('success');
  })
})

console.log(p6);
console.log(p7);
then(onResolved, onRejected) {
  if (typeof onRejected !== "function") {
    onRejected = (reason) => {
      throw reason;
    };
  }

  if (typeof onResolved !== "function") {
    onResolved = (value) => value;
  }

  // 以下部分代码微调
  return new MyPromise((resolve, reject) => {
    if (this.PromiseState === "fulfilled") {
      let result;

      setTimeout(() => {
        result = onResolved(this.PromiseResult);

        // resolvePromise判断回调得到的结果是不是MyPromise,是的话会继续执行then方法
        resolvePromise(result, resolve, reject);
      })

    } else if (this.PromiseState === "rejected") {
      setTimeout(() => {
        result = onRejected(this.PromiseResult);
        resolvePromise(result, resolve, reject);
      })

    } else if (this.PromiseState === 'pending') {
      this.resolvedQueue.push((() => {
        resolvePromise(onResolved(this.PromiseResult), resolve, reject)
      }));
      this.rejectedQueue.push(() => {
        resolvePromise(onResolved(this.PromiseResult), resolve, reject)
      });
    }
  })
}

resolvePromise

function resolvePromise(x, resolve, reject) {
  if (x instanceof MyPromise) {

    // 如果是`MyPromise`对象,则需要调用thrn方法,将状态变为 fulfilled 或者 rejecte
    x.then(resolve, reject);
  } else {
    // 普通值直接resolve就行
    resolve(x);
  }
}
const p5 = new MyPromise((resolve, reject) => {
  resolve('123456');
})

const p6 = p5.then(() => {
  return 123567;
});

const p7 = p6.then(() => {
  return new MyPromise((resolve, reject) => {
    resolve('success');
  })
})

console.log(p6);
console.log(p7);

catch方法的实现

在搞完上面的then方法后,catch方法就迎刃而解了。因为catch实际上就是一个语法糖,我们也可以用then方法来实现,只需要不传第一个参数,只传第二个参数即可。

catch(onRejected) {
  return this.then(undefined, onRejected);
}
new MyPromise((resolve, reject) => {
  reject('error');
})
  .catch((reason) => {
    console.error(reason);
  })
class MyPromise {
  constructor(executor) { // 执行器函数executor
    // Promise对象的状态
    this.PromiseState = 'pending';

    // Promise对象的结果
    this.PromiseResult = undefined;

    this.resolvedQueue = [];
    this.rejectedQueue = [];

    // 需要有`resolve`和`reject`函数,因为实例化Promise对象时需要传参`(resolve, reject) => { }`形式的函数
    const resolve = (data) => {
      if (this.PromiseState === 'pending') {
        // 只能从`pending`状态变为`resolved`
        this.PromiseState = 'fulfilled';
        this.PromiseResult = data;

        this.resolvedQueue.forEach(callback => callback(data));
      }
    };

    const reject = (data) => {
      if (this.PromiseState === 'pending') {
        // 只能从`pending`状态变为`rejected`
        this.PromiseState = 'rejected';
        this.PromiseResult = data;

        this.rejectedQueue.forEach(callback => callback(data));
      }
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onResolved, onRejected) {
    if (typeof onRejected !== "function") {
      onRejected = (reason) => {
        throw reason;
      };
    }

    if (typeof onResolved !== "function") {
      onResolved = (value) => value;
    }

    return new MyPromise((resolve, reject) => {
      if (this.PromiseState === "fulfilled") {
        let result;

        setTimeout(() => {
          result = onResolved(this.PromiseResult);
          resolvePromise(result, resolve, reject);
        })

      } else if (this.PromiseState === "rejected") {
        setTimeout(() => {
          result = onRejected(this.PromiseResult);
          resolvePromise(result, resolve, reject);
        })

      } else if (this.PromiseState === 'pending') {
        this.resolvedQueue.push((() => {
          resolvePromise(onResolved(this.PromiseResult), resolve, reject)
        }));
        this.rejectedQueue.push(() => {
          resolvePromise(onResolved(this.PromiseResult), resolve, reject)
        });
      }
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
}

function resolvePromise(x, resolve, reject) {
  if (x instanceof MyPromise) {

    // 如果是`MyPromise`对象,则需要调用then方法,将状态变为 fulfilled 或者 rejecte
    x.then(resolve, reject);
  } else {
    // 普通值直接resolve就行
    resolve(x);
  }
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK