LoginSignup
0
0

More than 5 years have passed since last update.

Promiseとは2

Posted at

前回のまとめ

subscribersが登録されていないとpromiseには何も起こらない

いつsubscribersが登録されるのか?

それはthen

then

export default function then(onFulfillment, onRejection) {
  const parent = this;

  const child = new this.constructor(noop);

  if (child[PROMISE_ID] === undefined) {
    makePromise(child);
  }

  const { _state } = parent;

  if (_state) {
    const callback = arguments[_state - 1];
    asap(() => invokeCallback(_state, child, callback, parent._result));
  } else {
    subscribe(parent, child, onFulfillment, onRejection);
  }

  return child;
}

新しいPromiseが作られる。
呼び出し元から_stateを取り出し、_state !== PENDINGかどうかで処理が分かれる。

この時点で_state === PENDINGである場合

初期化時にresolverを渡さない場合は_stateが変化しないのでPENDINGのままthenが呼ばれる。
このときsubscribe(parent, child, onFulfillment, onRejection)が実行される。

function subscribe(parent, child, onFulfillment, onRejection) {
  let { _subscribers } = parent;
  let { length } = _subscribers;

  parent._onerror = null;

  _subscribers[length] = child;
  _subscribers[length + FULFILLED] = onFulfillment;
  _subscribers[length + REJECTED]  = onRejection;

  if (length === 0 && parent._state) {
    asap(publish, parent);
  }
}

ここで_subscribersに登録される。
1度のsubscribeでchild, fulfill, rejectが登録される。
resolverがない場合はparent._state === PENDINGなので何も実行されない。
ここまででresolverがない状態でPromiseを初期化するのは意味がないことがわかった。
つまり、初期化時は_subscribersは登録されないままで良い。

_state !== PENDINGである場合

resolverがある場合かつresolveが呼ばれた場合はここで_state === FULFILLEDなので、callback = onFulfillmentとなり、asap(() => invokeCallback(_state, child, callback, parent._result))が実行される。
asapはqueueのスケジューリングなのでここでは無視していく。invokeCallbackが重要。

function invokeCallback(settled, promise, callback, detail) {
  let hasCallback = isFunction(callback),
      value, error, succeeded, failed;

  if (hasCallback) {
    value = tryCatch(callback, detail);

    if (value === TRY_CATCH_ERROR) {
      failed = true;
      error = value.error;
      value = null;
    } else {
      succeeded = true;
    }

    if (promise === value) {
      reject(promise, cannotReturnOwn());
      return;
    }

  } else {
    value = detail;
    succeeded = true;
  }

  if (promise._state !== PENDING) {
    // noop
  } else if (hasCallback && succeeded) {
    resolve(promise, value);
  } else if (failed) {
    reject(promise, error);
  } else if (settled === FULFILLED) {
    fulfill(promise, value);
  } else if (settled === REJECTED) {
    reject(promise, value);
  }
}

callbackが関数ではない場合は、引き続きchildに対してfulfillが呼ばれて無限ループになり何も起こらない・・・
callbackが関数の場合は、callback(detail)がtry-catch内で実行される。
実行結果がchildのPromiseに渡され処理がつづく。

次回

resolveにobjectまたはfunctionを渡す場合の処理を読んでいく

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0