Реализовать обещание (на основе спецификации Promise/A+)

JavaScript

предисловие

Я считаю, что все часто используютPromiseили используйтеGenerator,asnyc/awaitи т.д. Асинхронные решения, онлайнPromiseПринципы расцветают повсюду.
Я всегда хотел найти время, чтобы написатьPromiseПонял, но обычная работа тоже очень загружена.Выпало 3 дня выходных на Новый год, а перерыв 2,5 дня.Полдня взял посмотреть.Promise.

Как использовать обещания

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000)
}).then((data) => {
  console.log(data);
  return new Promise((res) => {
    setTimeout(() => {
      res(2);
    },1000)
  })

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

период, термин

  • Promiseявляется совместимымpromiseТехнические характеристикиthenметод объекта или функции.
  • thenableвключенthenметод объекта или функции.
  • valueлюбойJavascriptстоимость. (включатьundefined, thenable, PromiseЖдать).
  • exceptionКthrowЗначение, выдаваемое выражением.
  • reasonэто описаниеPromiseЗначение причины отказа.

из-заPromise/A+Спецификация не включаетcatch,race,allИ других способов добиться, так что тут подробно не объяснять.

Требовать

ОдинPromiseДолжен находиться в одном из следующих состояний:pending, fulfilledилиrejected. еслиpendingстатус, тоpromise:

может быть преобразован вfulfilledилиrejectedусловие. еслиfulfilledстатус, тоpromise:

  • Невозможно перейти в любое другое состояние.
  • Должно быть значение, и это значение нельзя изменить.

еслиrejectedстатус, тоpromiseМогу:

  • Невозможно перейти в любое другое состояние.
  • Должна быть причина, и это значение нельзя изменить.
function MyPromise(callback) {

  let that = this;
  //定义初始状态
  //Promise状态
  that.status = 'pending';
  //value
  that.value = 'undefined';
  //reason 是一个用于描述Promise被拒绝原因的值。
  that.reason = 'undefined';

  //定义resolve
  function resolve(value) {
    //当status为pending时,定义Javascript值,定义其状态为fulfilled
    if(that.status === 'pending') {
      that.value = value;
      that.status = 'resolved';
    }
  }

  //定义reject
  function reject(reason) {
    //当status为pending时,定义reason值,定义其状态为rejected
    if(that.status === 'pending') {
      that.reason = reason;
      that.status = 'rejected';
    }
  }

  //捕获callback是否报错
  try {
    callback(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

Promiseобъект имеетthenметод, используемый для регистрации в этомPromiseОбратный вызов после определения статуса,thenМетод принимает два параметра:Promise.then(onFulfilled,onRejected).
Мы пишем функцию then на прототипе.

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  let that = this;

  if(that.status === 'resolved') {
    onFulfilled(that.value);
  }

  if(that.status === 'rejected') {
    onRejected(that.reason);
  }
}

Приведенный выше код просто реализуетPromiseСамая основная логика , если вы напрямую вызываетеthenявляется исполняемым, но не поддерживает асинхронность, иPromiseСамая большая особенность - решитьcallbackПроблема с адским асинхронным обратным вызовом.
Итак, переделываем.

function MyPromise(callback) {

  let that = this;
  //定义初始状态
  //Promise状态
  that.status = 'pending';
  //value
  that.value = 'undefined';
  //reason 是一个用于描述Promise被拒绝原因的值。
  that.reason = 'undefined';
  //用来解决异步问题的数组
  that.onFullfilledArray = [];
  that.onRejectedArray = [];

  //定义resolve
  function resolve(value) {
    //当status为pending时,定义Javascript值,定义其状态为fulfilled
    if(that.status === 'pending') {
      that.value = value;
      that.status = 'resolved';
      that.onFullfilledArray.forEach((func) => {
        func(that.value);
      });
    }
  }

  //定义reject
  function reject(reason) {
    //当status为pending时,定义reason值,定义其状态为rejected
    if(that.status === 'pending') {
      that.reason = reason;
      that.status = 'rejected';
      that.onRejectedArray.forEach((func) => {
        func(that.reason);
      });
    }
  }

  //捕获callback是否报错
  try {
    callback(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

thenмодификация функции

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  let that = this;
  //需要修改下,解决异步问题,即当Promise调用resolve之后再调用then执行onFulfilled(that.value)。
  //用两个数组保存下onFulfilledArray
  if(that.status === 'pending') {
    that.onFullfilledArray.push((value) => {
      onFulfilled(value);
    });

    that.onRejectedArray.push((reason) => {
      onRejected(reason);
    });
  }

  if(that.status === 'resolved') {
    onFulfilled(that.value);
  }

  if(that.status === 'rejected') {
    onRejected(that.reason);
  }
}

из-заPromise/A+Спецификация предусматриваетPromiseДолжен находиться в одном из следующих состояний:pending, fulfilledилиrejected, поэтому, когда пользователь используетPromiseКогда написан асинхронный код, то в это времяPromiseдолжен быть вpendingСтатус, в остальном нормальный звонок.
Поэтому инициализируйтеPromise, определите два массива какonFullfilledArray,onRejectedArray, сохранитьthenДве функции обратного вызова функцииonFulfilledа такжеonRejected. В то же время мыthenСуждение в функцииstatusтак или иначеpending,ПотомonFulfilledа такжеonRejectedв соответствующие массивы соответственно. когда пользователь звонитresolveилиreject, изменить состояние, перебрать массив, выполнитьonFulfilledилиonRejected, который вызывается асинхронно.

Цепные вызовы функции then

существуетPromise/A+В спецификации:

Для промиса его метод then может вызываться несколько раз.

  • когдаpromise fulfilledпосле всегоonFulfilledВсе должно быть выполнено в том порядке, в котором они были зарегистрированы.
  • когдаpromise rejectedпосле всегоOnRejectedВсе должно быть выполнено в том порядке, в котором они были зарегистрированы.

затем должен вернуть обещание

  • еслиonFulfilledилиonRejectedвозвращаемое значениеx, затем выполнитеPromiseПроцесс разбора[[Resolve]](promise2, x).
  • еслиonFulfilledилиonRejectedИсключение было брошеноe, ноpromise2должно бытьeдляreasonбыть отвергнутым.
  • еслиonFulfilledне является функцией иpromise1ужеfulfilled,ноpromise2должен начинаться сpromise1значениеfulfilled.
  • еслиOnRejectне является функцией иpromise1ужеrejected, ноpromise2должно быть таким жеreasonбыть отвергнутым.
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  let that = this;
  let promise2;

  // 根据标准,如果then的参数不是function,则我们需要忽略它
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) {};
  onRejected = typeof onRejected === 'function' ? onRejected : function(r) {};
  //需要修改下,解决异步问题,即当Promise调用resolve之后再调用then执行onFulfilled(that.value)。
  //用两个数组保存下onFulfilledArray
  if(that.status === 'pending') {

    return promise2 = new Promise(function(resolve, reject) {
      that.onFullfilledArray.push((value) => {
      try {
        let x = onFulfilled(that.value);
        //判断onFulfilled是否是一个Promise,如果是,那么就直接把MyPromise中的resolve和reject传给then;
        //返回值是一个Promise对象,直接取它的结果做为promise2的结果
        if(x instanceof MyPromise) {
          x.then(resolve, reject);
        }
        //否则,以它的返回值做为promise2的结果
        resolve(x);
      } catch (error) {
        reject(error);
      }
      });

      that.onRejectedArray.push((value) => {
          try {
            let x = onRejected(that.value);
            //判断onRejected是否是一个Promise,如果是,那么就直接把MyPromise中的resolve和reject传给then;
            //返回值是一个Promise对象,直接取它的结果做为promise2的结果
            if(x instanceof MyPromise) {
              x.then(resolve, reject);
            }
            //否则,以它的返回值做为promise2的结果
            resolve(x);
          } catch (error) {
            reject(error);
          }
      });

    })
  }

  if(that.status === 'fulfilled') {
    return promise2 = new MyPromise(function(resolve, reject) {
      try {
        let x = onFulfilled(that.value);
        //判断onFulfilled是否是一个Promise,如果是,那么就直接把MyPromise中的resolve和reject传给then;
        //返回值是一个Promise对象,直接取它的结果做为promise2的结果
        if(x instanceof MyPromise) {
          x.then(resolve, reject);
        }
        //否则,以它的返回值做为promise2的结果
        resolve(x);
      } catch (error) {
        reject(error);
      }
      
    })
  }

  if(that.status === 'rejected') {
    return new MyPromise(function(resolve, reject) {
      try {
        let x = onRejected(that.value);
        //判断onRejected是否是一个Promise,如果是,那么就直接把MyPromise中的resolve和reject传给then;
        //返回值是一个Promise对象,直接取它的结果做为promise2的结果
        if(x instanceof MyPromise) {
          x.then(resolve, reject);
        }
        //否则,以它的返回值做为promise2的结果
        resolve(x);
      } catch (error) {
        reject(error);
      }
      
    })
    
  }
}

вызовthenкогда, судитьonFulfilledа такжеonRejectedЯвляется ли это функцией, если нет, возвращайте анонимную функцию и должны возвращать значение каждого параметра, используемого для решения цепочки вызовов.PromiseЗначение претендента.
Например:

new MyPromise(resolve=>resolve(8))
  .then()
  .then()
  .then(function foo(value) {
    alert(value)
  })

Итак, мы изменили блок на этот:

  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f};
  onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r};

Но это нужно переписывать каждый разxа такжеMyPromiseотношения, поэтому нам нужно абстрагировать этот фрагмент кода, этот фрагмент кода вPromise/A+называется в спецификацииresolvePromise.

function resolvePromise(promise, x, resolve, reject) {
  let then,thenCalledOrThrow = false
  //如果promise 和 x 指向相同的值, 使用 TypeError做为原因将promise拒绝。
  if (promise === x) {
    return reject(new TypeError('Chaining cycle detected for promise!'))
  }

  //判断x是否是一个Promise,如果是,那么就直接把MyPromise中的resolve和reject传给then;
  //返回值是一个Promise对象,直接取它的结果做为promise2的结果
  if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
    try {
      then = x.then
      if (typeof then === 'function') { // typeof 

        //x.then(resolve, reject);
        then.call(x, function rs(y) {

          if (thenCalledOrThrow) return

          thenCalledOrThrow = true

          return resolvePromise(promise, y, resolve, reject)

        }, function rj(r) {

          if (thenCalledOrThrow) return

          thenCalledOrThrow = true

          return reject(r)

        })
      } else {

        return resolve(x)
      }
    } catch(e) {
      if (thenCalledOrThrow) return

      thenCalledOrThrow = true

      return reject(e)
    }
  } else {

    return resolve(x)
  }

}

thenНаконец, функция изменена на:

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  let that = this;
  let promise2;
  // 根据标准,如果then的参数不是function,则我们需要忽略它
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(f) { return f};
  onRejected = typeof onRejected === 'function' ? onRejected : function(r) {throw r};
  //需要修改下,解决异步问题,即当Promise调用resolve之后再调用then执行onFulfilled(that.value)。
  //用两个数组保存下onFulfilledArray
  if(that.status === 'pending') {
    return promise2 = new Promise(function(resolve, reject) {
      that.onFullfilledArray.push((value) => {
        try {
          let x = onFulfilled(value);
          resolvePromise(promise2, x, resolve, reject)
        } catch(e) {
          return reject(e)
        }
      });
      
      that.onRejectedArray.push((value) => {
        try {
          let x = onRejected(value);
          resolvePromise(promise2, x, resolve, reject)
        } catch(e) {
          return reject(e)
        }
      });
    })
  }

  if(that.status === 'fulfilled') {
    return promise2 = new MyPromise(function(resolve, reject) {
      try {
        let x = onFulfilled(that.value);
        //处理then的多种情况
        resolvePromise(promise2, x, resolve, reject)
      } catch (error) {
        reject(error);
      }
    })
  }

  if(that.status === 'rejected') {
    return new MyPromise(function(resolve, reject) {
      try {
        let x = onRejected(that.value);
        //处理then的多种情况
        resolvePromise(promise2, x, resolve, reject);
      } catch (error) {
        reject(error)
      }
      
    })
    
  }
}

есть тест:

new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000)
}).then((data) => {
  console.log(data);
  return new MyPromise((res) => {
    setTimeout(() => {
      res(2);
    },1000)
  })
}).then((res) => {
  console.log(res);
})
//1
//2

наконец

Обещайте адрес GITHUB.
Использованная литература:

«Обещание / Спецификация A +»
"Обещание3"
«Реализация промиса, полностью соответствующего спецификации Promise/A+»
«Анализируйте внутреннюю структуру Promise, шаг за шагом, чтобы создать полный класс Promise, способный пройти все тестовые случаи»