Заходите и посмотрите самую полную рукописную реализацию ES6 Promise

JavaScript
Заходите и посмотрите самую полную рукописную реализацию ES6 Promise

напишите в начале

  • Я видел несколько технических групп за последние несколько днейPromiseНекоторые связанные реализации, я недавно смотрел на нихES6Некоторое содержание, так что я планирую разобраться сам, чтобы улучшить свое понимание;
  • Эта статья подходит для некоторых людей, которые знают и используютPromiseлюди, если вы не знаете или не используетеPromise, рекомендуется посмотретьОбещание Руана Ифэна о начале работы с ECMAScript6.

чтоPromise

  • Решение для асинхронного программирования;
  • Promiseэто контейнер, содержащий результат события (обычно асинхронной операции), которое завершится в будущем;

Функции

  • На состояние объекта не влияет внешний мир.PromiseОбъект представляет собой асинхронную операцию и имеет три состояния:pending(в ходе выполнения),fulfilled(успешно) иrejected(не удалось).
  • Однажды состояние изменилось, оно больше не изменится, и этот результат можно получить в любой момент.PromiseЕсть только две возможности для изменения состояния объекта: отpendingсталиfulfilledи изpendingсталиrejected.

Простая реализация

Анализ процесса

  • Источник изображенияMDN

инициализироватьPromise

  • оригинальныйPromise
// Promise构造函数接收一个executor函数,executor函数执行完同步或异步操作后,调用它的两个参数resolve和reject
const promise = new Promise(function(resolve, reject) {
  /*
    如果操作成功,调用resolve并传入value
    如果操作失败,调用reject并传入reason
  */
})
  • содержит текущее состояние
  • текущая стоимость
  • fulfilledмассив callback-функций
  • rejectedмассив callback-функций
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

const NewPromise = function(executor) {
  const _this = this;
  _this.status = PENDING; // 状态
  _this.data = undefined; // 值
  _this.onResolvedCallback = []; // fulfilled的回调函数数组
  _this.onRejectedCallback = []; // rejected的回调函数数组
  // 成功
  function resolve(value) { ... }
  // 失败
  function reject(reason) { ... }
  try {
    executor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}  

Полныйresoleа такжеreject

  • resoleявляется успешным вызовом и не требует изменения состояния дляfulfilled, то текущее значение устанавливается на входящее значение, и выполняется обходonResolvedCallbackобратный звонок в .
// 成功
function resolve(value) {
    只有pending状态才能转换为fulfilled / rejected
    setTimeout(function() { // 异步执行所有的回调函数
        if (_this.status === PENDING) {
            _this.status = FULFILLED;
            _this.data = value;
            _this.onResolvedCallback.forEach(callback => callback(value));
        }
    });
}
  • rejectреализация иresolveАналогично, только состояние изменяется наrejectedи реализацияonRejectedCallback.
// 失败
function reject(reason) {
    setTimeout(function() { // 异步执行所有的回调函数
        if (_this.status === PENDING) {
            _this.status = REJECTED;
            _this.data = reason;
            _this.onRejectedCallback.forEach(callback => callback(reason));
        }
    });
}
  • Основы теперь реализованыPromise, вы можете использовать какPromiseИспользуйте его таким же образом, но еще не добавляйте другой метод.
// 测试
const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11)
})

thenметод

  • Promiseобъект имеетthenМетод, используемый в этой регистрацииPromiseОбратный вызов после определения состояния.thenМетод нужно прописать на цепочке прототипов (почему на прототипе непонятно, может надо дополнитьJavaScriptбазовый). существуетPromise/Aстандартный, четко оговариваетсяthenВозвращать новый объект, поэтому в нашей реализации также возвращаем новый объект.
// then挂载到原型上
NewPromise.prototype.then = function(onResolved, onRejected) {
  const _this = this;
  if ( typeof onResolved !== 'function') {
   onResolved = function(value) { return value }
  }
  if ( typeof onRejected !== 'function') {
    onRejected = function(reason) { throw reason }
  }

  // 公共判断
  const common = function (data, resolve, reject) {
    // 考虑到有可能throw,我们将其包在try/catch块里
    try {
      let value = _this.status === FULFILLED
        ? onResolved(data)
        : onRejected(data)
      if( value instanceof Promise) {
        value.then(resolve, reject)
      }
      resolve(value)
    } catch (error) {
      reject(error)
    }
  }
  // 公共判断
  const pendingCommon = function (data, flag, resolve, reject) {
    // 考虑到有可能throw,我们将其包在try/catch块里
    try {
      let value = flag === FULFILLED
        ? onResolved(data)
        : onRejected(data)
      if( value instanceof Promise) {
        value.then(resolve, reject)
      }
      resolve(value)
    } catch (error) {
      reject(error)
    }
  }

  if (_this.status === PENDING) {
    return new NewPromise(function(resolve, reject) {
      _this.onResolvedCallback.push((value) => {
        pendingCommon(value, FULFILLED, resolve, reject);
      })

      _this.onRejectedCallback.push((reason) => {
        pendingCommon(reason, REJECTED, resolve, reject);
      })
    })
  } else { // resolve / reject
    return new NewPromise(function (resolve, reject) {
      setTimeout(function () {
        common(_this.data, resolve, reject)
      })
    })
  }
}

правильноresolveМетод снова усовершенствован

  • судитьrosolveЯвляется ли значениеPromise,еслиPromiseПродолжать.thenметод.
// 成功
function resolve(value) { 
    // value 如果是Promise继续执行.then
+   if (value instanceof Promise) {
+     return value.then(resolve, reject)
+   }
    ...
}

catchметод

  • захватыватьPromiseошибка исключения.
// catch方法
NewPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}

finallyметод

  • finally()метод используется, чтобы указать, является лиPromiseОперация будет выполнена независимо от конечного состояния объекта.
// finally
NewPromise.prototype.finally = function (fun) {
  return this.then((value) => {
      return NewPromise.resolve(fun()).then(() => value);
  }, (err) => {
      return NewPromise.resolve(fun()).then(() => {
          throw err;
      });
  });
};

resolveметод

  • Существующие объекты преобразуются вPromiseобъект, состояние экземпляраfulfilled.
NewPromise.resolve = function(value) {
  if (value instanceof Promise) return value;
    if (value === null) return null;
    // 判断如果是promise
    if (typeof value === 'object' || typeof value === 'function') {
        try {
            // 判断是否有then方法
            let then = value.then;
            if (typeof then === 'function') {
                return new NewPromise(then.call(value)); // 执行value方法
            }
        } catch (e) {
            return new NewPromise( (resolve, reject) =>{
                reject(e);
            });
        }
    }
    return new NewPromise( (resolve, reject) =>{
      resolve(value);
  });
}

rejectметод

  • Существующие объекты преобразуются вPromiseобъект, состояние экземпляраrejected. достичь иresolveМетод аналогичен, и окончательный возврат изменен наreject.
NewPromise.reject = function(reason) {
    ...
    return new NewPromise( (resolve, reject) =>{
      reject(reason);
  });
}

allметод

  • Promise.allМетод используется для переноса нескольких экземпляров Promise в новый.PromiseЭкземпляр; аргументы метода не могут быть массивами, но должны иметьIteratorинтерфейс, и каждый возвращаемый элементPromiseпример.
// all
NewPromise.all = function(promises) {
  return new NewPromise((resolve, reject) => {
    const result = []
    // 判断参数属否是数组
    promises = Array.isArray(promises) ? promises : []
    const len = promises.length;
    if(len === 0) resolve([]);
    let count = 0
    for (let i = 0; i < len; ++i) {
      if(promises[i] instanceof Promise) {
          promises[i].then(value => {
            count ++
            result[i] = value
            if (count === len) resolve(result)
          }, reject)
      } else {
        count ++
        result[i] = promises[i]
        if (count === len) resolve(result)
      }
    }
  }) 
}

raceметод

  • Promise.race()Метод также заключается в объединении несколькихPromiseэкземпляр, завернутый в новыйPromiseпример.
  • Пока один из параметров первым меняет состояние,Promise.race()статус меняется соответственно. Первый, кто изменилPromiseВозвращаемое значение экземпляра передается вPromise.race()функция обратного вызова.
// race
NewPromise.race = function (promises) {
  return new NewPromise((resolve, reject) => {
    promises = Array.isArray(promises) ? promises : []
    promises.forEach(promise => {
      promise.then(resolve, reject)
    })
  })
}

deferredметод

  • Возвращает объект с помощью метода обещания.
// defered
NewPromise.deferred = function() {
  const dfd = {}
  dfd.promise = new NewPromise(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

allSettledметод

  • Promise.allSettled()метод принимает наборPromiseэкземпляр как параметр, завернутый в новыйPromiseэкземпляр, экземпляр оболочки не завершится, пока все эти экземпляры параметров не вернут результаты, независимо от того, выполнены они или отклонены. Возвращает массив, содержащий все результаты.
NewPromise.allSettled = function (promises) {
  return new NewPromise((resolve, reject) => {
    promises = Array.isArray(promises) ? promises : []
    let len = promises.length;
    const argslen = len;
    if (len === 0) return resolve([]);
    let args = Array.prototype.slice.call(promises);
    function resolvePromise(index, value) {
      if(typeof value === 'object') { // 传入的是否是object
        const then = value.then;
        if(typeof then === 'function'){
          then.call(value, function(val) {
            args[index] = { status: 'fulfilled', value: val};
            if(--len === 0) {
              resolve(args);
            }
          }, function(e) {
            args[index] = { status: 'rejected', reason: e };
            if(--len === 0) {
              reject(args);
            }
          })
        }
      }
    }
 
    for(let i = 0; i < argslen; i++){
      resolvePromise(i, args[i]);
    }
  })
}

тестовое задание

Базовый тест

// 测试
const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11);
  resolve(123);
})

thenтестовое задание

promise.then(val => {
  console.log('val', val)
})

catchтестовое задание

const promise = new NewPromise(function(resolve, reject) {
  console.log('ss', 11);
  reject('errr')
})
promise.catch(err => {
  console.log('err', err)
})

finallyтестовое задание

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
}).catch(err => {
  console.log('err', err)
}).finally(() => {
  console.log('finally')
})

resolveтестовое задание

const resolved = NewPromise.resolve(1);
resolved.then(val => {
    console.log('resolved', val)
})

rejectтестовое задание

const rejected = NewPromise.reject(-1);
rejected.catch(val => {
  console.log('rejected', val)
})

allтестовое задание

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.all([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
}).catch(err => {
  console.log('err', err)
})

allSettledтестовое задание

const resolved = NewPromise.resolve(1);
const rejected = NewPromise.reject(-1);
const resolved1 = NewPromise.resolve(17);

const p = NewPromise.allSettled([resolved, resolved1, rejected]);
p.then((result) => {
  console.log('result', result)
})

весь код

Ссылаться на

Руан Ифэн: Начало работы с ECMAScript 6
22 высокочастотных рукописных вопроса и ответа на JavaScript для интервью

  • Если это поможет вам, ставьте лайк 👍, ваша поддержка является движущей силой для моего выхода.

Пополнить

правильноPromise.allСомнительный

  • проверенный роднойPromise.allметод.

  • когда все выполнено

Изменить асинхронный успешный порядок возврата метода all

  • Весь приведенный выше код метода был обновлен.
  • тестовый пример, спасибоAshoneAпредложенный вопрос.
const promise1 = new Promise(resolve => {
  setTimeout(() => {
    resolve("promise1");
  }, 200);
});
const promise2 = new Promise(resolve => {
  setTimeout(() => {
    resolve("promise2");
  }, 100);
});
NewPromise.all([promise1, promise2]).then(value => {
  console.log(value);
});

Promise.all([promise1, promise2]).then(value => {
  console.log(value);
});
  • предыдущие результаты испытаний. [тестовый адрес](

код sandbox.io/is/musing-rough…)

  • Измененные результаты теста.