MyPrototypeWhat / take-down

记一些平时的笔记 有笔记 有源码解析
9 stars 0 forks source link

语法糖解析系列之——Promise部分实现 #16

Open MyPrototypeWhat opened 2 years ago

MyPrototypeWhat commented 2 years ago

Promise部分实现

实现构造函数(resolve情况)\Promise.resolve,未实现函数后续再更新

const share = new WeakMap();
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
let call = Function.prototype.call;
call = Function.prototype.call ? call.bind(call) : () => {};

function bind(fn, state, wrapper) {
  return function (value) {
    fn(state, value, wrapper);
  };
}

function getInternalState(key) {
  return share.get(key);
}

var Queue = function () {
  this.head = null;
  this.tail = null;
};

Queue.prototype = {
  add: function (item) {
    var entry = { item: item, next: null };
    if (this.head) this.tail.next = entry;
    else this.head = entry;
    this.tail = entry;
  },
  get: function () {
    var entry = this.head;
    if (entry) {
      this.head = entry.next;
      if (this.tail === entry) this.tail = null;
      return entry.item;
    }
  },
};

function internal(callback) {
  if (share.get(this)) {
    return share.raw;
  }
  share.set(this, {
    done: false,
    notified: false,
    parent: false,
    reactions: new Queue(),
    rejection: false,
    state: PENDING,
    value: undefined,
    raw: this,
  });
}

function callReaction(reaction, state) {
  var value = state.value;
  var ok = state.state == FULFILLED;
  // 对应then中参数 reaction.ok没传默认为true
  var handler = ok ? reaction.ok : reaction.fail;
  var resolve = reaction.resolve;
  var reject = reaction.reject;
  var domain = reaction.domain;
  var result, then, exited;
  try {
    if (handler) {
      // then(有参数)情况
      if (!ok) {
        //
        if (state.rejection === UNHANDLED) onHandleUnhandled(state);
        state.rejection = HANDLED;
      }
      // FULFILLED并且then(没有第一个参数)情况
      if (handler === true) result = value;
      else {
        // node环境下
        if (domain) domain.enter();
        // 执行then中函数
        result = handler(value); // can throw
        if (domain) {
          domain.exit();
          exited = true;
        }
      }
      if (result === reaction.promise) {
        // result对应resolve()中传递的参数
        // result为当前promise实例
        reject(TypeError("Promise-chain cycle"));
      }
      // thenable情况
      // else if ((then = isThenable(result))) {
      //   call(then, result, resolve, reject);
      // }
      else resolve(result);
    } else reject(value); // ok为false 并且then中未传第二个参数
  } catch (error) {
    if (domain && !exited) domain.exit();
    reject(error);
  }
}

function notify(state, isReject) {
  if (state.notified) return;
  state.notified = true;
  queueMicrotask(function () {
    var reactions = state.reactions;
    var reaction;
    while ((reaction = reactions.get())) {
      callReaction(reaction, state);
    }
    state.notified = false;
    if (isReject && !state.rejection) onUnhandled(state);
  });
}

function newPromiseCapability(C) {
  let resolve, reject;
  this.promise = new C(function ($$resolve, $$reject) {
    resolve = $$resolve;
    reject = $$reject;
  });
  this.resolve = resolve;
  this.reject = reject;
}

function isCallable(argument) {
  return typeof argument == "function";
}

function isObject(it) {
  return typeof it == "object" ? it !== null : isCallable(it);
}

var isThenable = function (it) {
  var then;
  return isObject(it) && isCallable((then = it.then)) ? then : false;
};

function PromiseConstructor(executor) {
  internal.call(this);
  const state = getInternalState(this);
  executor(bind(internalResolve, state), bind(internalReject, state));
}

function internalResolve(state, value, wrapper) {
  if (state.done) return;
  state.done = true;
  if (wrapper) state = wrapper;
  if (state.raw === value) return; // 报错
  // 忽略thenable情况
  var then = isThenable(value);
  if (then) {
    queueMicrotask(function () {
      var wrapper = { done: false };
      // try {
      call(
        then,
        value,
        bind(internalResolve, wrapper, state),
        bind(internalReject, wrapper, state)
      );
      // }
      // catch (error) {
      //   internalReject(wrapper, error, state);
      // }
    });
  } else {
    state.value = value;
    state.state = FULFILLED;
    notify(state, false);
  }
}

function internalReject() {}

PromiseConstructor.prototype.then = function (onFulfilled, onRejected) {
  var state = getInternalState(this);
  var reaction = new newPromiseCapability(PromiseConstructor);
  state.parent = true;
  reaction.ok = isCallable(onFulfilled) ? onFulfilled : true;
  reaction.fail = isCallable(onRejected) && onRejected;
  // node环境
  // reaction.domain = IS_NODE ? process.domain : undefined;
  if (state.state == PENDING) state.reactions.add(reaction);
  // 如果当前state为pending则将新创建的promise实例添加进队列
  // 否则就当成微任务处理
  else
    queueMicrotask(function () {
      callReaction(reaction, state);
    });
  // 链式调用
  return reaction.promise;
};
PromiseConstructor.resolve = function resolve(x) {
  const p = new newPromiseCapability(PromiseConstructor);
  p.resolve(x);
  return p.promise;
};

const p = new PromiseConstructor((res) => {
  console.log(1);
  setTimeout(() => {
    res(2);
  }, 1000);
})
  .then((res) => {
    console.log("res", res);
    return PromiseConstructor.resolve("a");
  })
  .then((res) => {
    console.log(res);
  });
MyPrototypeWhat commented 5 months ago

callReaction

...
 if (result === reaction.promise) {
    reject(new TypeError('Promise-chain cycle'));
  } else if (then = isThenable(result)) {
    call(then, result, resolve, reject); // 相当于 result.then(resolve,reject)
  } else resolve(result);
...

例子:

.then(()=>new Promise((resolve)=>resolve())).then(()=>{console.log(123)})

123会在前一个Promise resolve之后输出

MyPrototypeWhat commented 5 months ago

try catch

无法被catch到的

try {
    new Promise((_,reject)=>{
        reject(1)
    })
} catch (error) {
    console.log('error',error)
}

reject错误并不能被捕获,因为reject中throw err部分在微任务中,也就是说trycatch执行完了,reject的错误才会抛出,所以无法捕获。 同理在:

new Promise((_,reject)=>{
        console.log(a) // a is not defined
    })

这个错误也是无法被catch到的,new Promise(executor),在执行executor时 外层有一个trycatchcatch到错误用reject抛错

如何解决

很简单,reject抛错变为同步,让catchreject

try {
    await new Promise((_,reject)=>{
        reject(1)
    })
} catch (error) {
    console.log('error',error) // error 1
}

为什么加个await就可以了呢

这就是涉及到了async await语法糖 将上面代码包装一下:

function* fn(){
  try {
      yield new Promise((_,reject)=>{
          reject(1)
      })
  } catch (error) {
      console.log('error',error) // error 1
  }
}

const gen=fn()
const info=gen.next() // { value:Promise, done:false }
if(info.done){
  resolve(info.value)
}else{
  // reject(1) 所以肯定会走到_throw逻辑中,_throw其实就是调用了gen.throw,将错误抛出
  Promise.resolve(info.value).then(_next,_throw) 
}

可能会有疑惑,.then(_next,_throw)不就是个异步了吗? 因为generator函数,yield会阻塞下面的执行,所以会等待直到.then的回调触发

MyPrototypeWhat commented 4 months ago

then 结果透传

例如

Promise.resolve(10)
  .then(2)
  .then(Promise.resolve(3))
  .then((res) => console.log(res)); // res输出什么呢?

答案是10,为什么呢? 如果只是应对面试话可以直接这么记:因为上面几个then内没有返回值

源码角度

core-js_then的源码

Promise.prototype.then = function () {
  // 通过this获取state
  var state = getInternalPromiseState(this);
  // 创建一个对象,包含promise,reject,resolve
  var reaction = newPromiseCapability(
    speciesConstructor(this, PromiseConstructor)
  );
  state.parent = true;
  // 如果onFulfilled不是函数,一律是true
  reaction.ok = isCallable(onFulfilled) ? onFulfilled : true;
  reaction.fail = isCallable(onRejected) && onRejected;
  reaction.domain = IS_NODE ? process.domain : undefined;
  // 添加到reaction队列中,当状态更改后,遍历reaction,执行callReaction
  if (state.state === PENDING) state.reactions.add(reaction);
  // 如果状态不为PENDING,则添加到微任务队列中
  else
    microtask(function () {
      callReaction(reaction, state);
    });
  // 注意这个!!返回的是这个then创建的promise
  return reaction.promise;
};

从上面的源码可看出,当then中传了2reaction.ok = true 再转到callReaction

// ...
// 前一个promise resolve的值
var value = state.value;
var ok = state.state === FULFILLED;
var handler = ok ? reaction.ok : reaction.fail;
var resolve = reaction.resolve;
var reject = reaction.reject;
var result;
// ...
if (handler) {
  // ...
  // 如果handler是true,则result = value
  if (handler === true) result = value;
}
if (result === reaction.promise) {
  reject(new TypeError("Promise-chain cycle"));
} else if ((then = isThenable(result))) {
  call(then, result, resolve, reject);
  // 看这里,这个resolve是then生成的promsie的resolve
} else resolve(result);
// ...

结合来看,无论 then 中传了什么,只要不是函数,就result = value,所以例子中2,Promise.resolve(3)都没用到,所以最后输出10