предисловие
Асинхронное поведение является фундаментальным для JavaScript, но предыдущие реализации были неоптимальными. В раннем JavaScript поддерживалось только определение функций обратного вызова, сигнализирующих о завершении асинхронной операции. Объединение нескольких асинхронных операций — распространенная проблема, для решения которой обычно требуются глубоко вложенные функции обратного вызова (известные как «обратные вызовы из ада»).
Чтобы решить проблему ада обратных вызовов, ES6 реализует Promise в соответствии со спецификацией Promises/A+, которая может выражать асинхронное поведение в процессе синхронного поведения.
С момента выпуска ES6 в 2015 году и по настоящее время промисы используются повсеместно в разработке.Возможно, концепция и использование промисов задавались в ходе интервью по разработке интерфейса от среднего до продвинутого уровня три года назад.Теперь вас обычно просят внедрить Promise, который не только проверяет ваше понимание концепции и использования Promise, но и проверяет ваше мастерство в синтаксисе ES6 и его применении.
Чтобы реализовать Обещание, вы должны следоватьPromise/A+Спецификация, все библиотеки классов Promise в отрасли следуют этой спецификации. Конечно, спецификация также немного усовершенствована в соответствии с бизнес-сценарием.Эта колонка начинается с бизнес-сценария и шаг за шагом ведет вас к реализации обещания.Запоминать спецификацию непросто.
Я надеюсь, что эта колонка придаст вам уверенности в ваших предварительных собеседованиях от среднего до продвинутого уровня.
1. Проанализируйте бизнес-сценарий Promise
Кратко разберем бизнес-сценарий Promise.
- Promise имеет три состояния Pending (выполняется), Fulfilled (успешно), Rejected (неудачно). Внешний мир не может изменить эти три состояния, и как только состояние изменится, оно уже не изменится снова.
Для удобства записи в этой колонке статус Pending называется в процессе, статус Fulfilled — успешно, а статус Rejected — не выполнен.
-
Создание экземпляра Promise требует передачи
executorфункция, бизнес-код находится вexecutorфункцию и, кроме того,executorФункция принимает два параметраresolveа такжеreject.resolveа такжеrejectЯвляется встроенной функцией конструктора промисов.new Promise((resolve, reject) =>{ //... 业务代码 }) -
существует
executorБизнес-код в функции выполнен успешно, вызовresolveизменить состояние промиса на успешное и передать результат успешного выполнения бизнес-кода промису через параметры. -
существует
executorВыполнение бизнес-кода в функции не удалось, и вызовrejectизменить состояние промиса на неудачное и передать причину сбоя выполнения бизнес-кода в промис через параметры. -
метод экземпляра
thenПервый параметр — это функция обратного вызова для успешного выполнения бизнес-кода, а второй параметр — это функция обратного вызова для сбоя выполнения бизнес-кода.Когда бизнес-код выполняется, соответствующая функция обратного вызова будет вызвана в соответствии с результатом выполнения. , и эти callback-функции получат дело.В качестве параметра используется результат выполнения кода. -
методом экземпляра
catchчтобы добавить функцию обратного вызова для сбоя выполнения бизнес-кода.
Давайте реализуем функции Promise одну за другой.
2. Сначала создайте конструктор Promise
Согласно приведенному выше анализу бизнес-сценария Promise, конструктор Promise должен иметь следующее содержимое:
-
Promise — это класс, созданный с использованием синтаксиса Class в ES6.
-
Конструктор промисов получает
executorфункция в качестве параметра и выполняется в нейexecutorфункция. -
Конструктор промисов имеет
resolveа такжеrejectвстроенный метод и передается в качестве параметраexecutorфункция. -
Установить свойство экземпляра
statusдля хранения состояния. -
встроенная функция
resolveМожет изменить статус на успешный, встроенная функцияrejectСостояние может быть изменено на «сбой», и после изменения состояния оно больше не изменится.
Кроме того, Symbol используется вне конструктора для создания трех состояний Pending (выполняется), Fulfilled (успешно) и Rejected (сбой).
// 用Symbol定义三种状态,防止外界改变状态。
const Pending = Symbol('Pending'); // 进行中
const Fulfilled = Symbol('Fulfilled'); // 已成功
const Rejected = Symbol('Rejected'); // 已失败
class Promise {
constructor(executor){
this.status = Pending;//存储 Promise 的状态
const resolve = () =>{
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if(this.status === Pending){
this.status = Fulfilled;
}
};
const reject = () =>{
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if(this.status === Pending){
this.status = Rejected;
}
};
executor(resolve,reject);
},
}
3. Предварительная реализация метода экземпляра then
лицо, как указано вышеthenПростой анализ бизнес-сценария экземплярного метода, вthenКогда функция обратного вызова вызывается в методе экземпляра,executorРезультат выполнения бизнес-кода в функцию передается в качестве параметра, затем следует добавить атрибут экземпляра для хранения результата выполнения бизнес-кода. В дополнение к выполнению успешных результатов с помощью встроенных методовresolveПараметр передается, а причина его невыполнения передается через встроенный методrejectпередаются параметры.
// 在这里用Symbol定义三种状态,防止外部改变状态
const Pending = Symbol('Pending'); // 进行中
const Fulfilled = Symbol('Fulfilled'); // 已成功
const Rejected = Symbol('Rejected'); // 已失败
class Promise {
constructor(executor) {
this.status = Pending;//存储 Promise 的状态
this.value = undefined;//存储executor函数中业务代码执行成功的结果
this.reason = undefined;//存储executor函数中业务代码执行失败的原因
const resolve = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Fulfilled;
this.value = value;
}
};
const reject = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Rejected;
this.reason = value;
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
if(this.status === Fulfilled){
if (onFulfilled && typeof onFulfilled === 'function') {
onFulfilled(this.value)
}
}
if(this.status === Rejected){
if (onRejected && typeof onRejected === 'function') {
onRejected(this.reason)
}
}
}
}
Проверьте это с помощью тестового примера:
const test = new Promise((resolve,reject) =>{
resolve('"执行成功"')
})
test.then(res =>{
console.log(res)
})
Вы можете напечатать «выполнено успешно» на консоли, но здесь обрабатываются только промисы для синхронных операций. если вexecutorЧто если в функцию будет передана асинхронная операция?
const test = new Promise((resolve,reject) =>{
setTimeout(() =>{
resolve('"执行成功"')
},1000)
})
test.then(res =>{
console.log(res)
})
Через 1 секунду вы обнаружите, что сообщение «выполнение успешно» не выводится на консоль, потому что вызовthenВ методе экземпляра состояние Promise равно Pending, хотя состояние Promise становится Fulfilled через 1 секунду, ноthenМетод экземпляра уже был вызван.
Итак, как контролироватьthenВремя выполнения функции обратного вызова в методе экземпляра. Этого можно достичь с помощью шаблона проектирования издатель-подписчик.
при звонкеthenВ методе экземпляра, если состояние Promise — Pending, функция обратного вызова успеха и функция обратного вызова ошибки сохраняются отдельно, а затемexecutorВыполнение асинхронной задачи в функции завершается, вызывая встроенный методresolveилиreject, в котором эти функции обратного вызова вызываются по очереди.
Согласно этой идее, снова измените код.
// 在这里用Symbol定义三种状态,防止外部改变状态
const Pending = Symbol('Pending'); // 进行中
const Fulfilled = Symbol('Fulfilled'); // 已成功
const Rejected = Symbol('Rejected'); // 已失败
class Promise {
constructor(executor) {
this.status = Pending;//存储 Promise 的状态
this.value = undefined;//存储executor函数中业务代码执行成功的结果
this.reason = undefined;//存储executor函数中业务代码执行失败的原因
this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
const resolve = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Fulfilled;
this.value = value;
// 依次调用成功回调函数
this.onFulfilled.forEach(fn=>fn());
}
};
const reject = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Rejected;
this.reason = value;
// 依次调用失败回调函数
this.onRejected.forEach(fn=>fn());
}
};
executor(resolve, reject);
}
then(onFulfilled, onRejected) {
if(this.status === Fulfilled){
if (onFulfilled && typeof onFulfilled === 'function') {
onFulfilled(this.value)
}
}
if(this.status === Rejected){
if (onRejected && typeof onRejected === 'function') {
onRejected(this.reason)
}
}
if(this.status === Pending){
if (onFulfilled && typeof onFulfilled === 'function') {
this.onFulfilled.push(() =>{
onFulfilled(this.value)
})
}
if (onRejected && typeof onRejected === 'function') {
this.onRejected.push(() =>{
onRejected(this.reason)
})
}
}
}
}
Протестируйте его с помощью приведенного выше тестового примера, через 1 секунду консоль напечатает «выполнение успешно». Логика кода правильная.
thenБизнес-целью метода экземпляра должно быть добавление функции обратного вызова при изменении состояния промиса, функция обратного вызова, статус которой стал успешным, добавляется через первый параметр, а функция обратного вызова, статус которой стал неудачным, передается через второй параметр Добавить в.
В-четвертых, реализация метода экземпляра, а затем микрозадачи
Поскольку нативный Promise — это микрозадача, предоставляемая движком V8, мы не можем восстановить реализацию движка V8, поэтому здесь мы используем setTimeout для имитации асинхронности, поэтому нативная — это микрозадача, а здесь — макрозадача.
Кроме того, в спецификации Promise A+ 3.1 также упоминается:
Здесь "код платформы" означает движок, среду и код реализации обещания. На практике это требование гарантирует, что onFulfilled и onRejected выполняются асинхронно, после поворота цикла событий, в котором затем вызывается, и с новым стеком. Это может быть реализовано с помощью либо с помощью механизма «макрозадач», такого как setTimeout или setImmediate, либо с помощью механизма «микрозадач», такого как MutationObserver или process.nextTick. Поскольку реализация обещания считается кодом платформы, она может сама содержать очередь планирования задач или "батут", в котором вызываются обработчики.
переводить
Этого можно достичь с помощью механизма «макрозадачи» (например, setTimeout или setImmediate) или механизма «микрозадачи» (например, MutatonObserver или process.nextTick ).
Если вы хотите реализовать микропрограммы Promise, вы можете заменить seitimeout на реализацию микросуры. Здесь просто имитация асинхронности.
then(onFulfilled, onRejected) {
if (this.status === Fulfilled) {
if (onFulfilled && typeof onFulfilled === 'function') {
setTimeout(() => {
onFulfilled(this.value)
}, 0)
}
}
if (this.status === Rejected) {
if (onRejected && typeof onRejected === 'function') {
setTimeout(() => {
onRejected(this.reason)
}, 0)
}
}
if (this.status === Pending) {
if (onFulfilled && typeof onFulfilled === 'function') {
this.onFulfilled.push(() => {
setTimeout(() => {
onFulfilled(this.value)
}, 0)
})
}
if (onRejected && typeof onRejected === 'function') {
this.onRejected.push(() => {
setTimeout(() => {
onRejected(this.reason)
}, 0)
})
}
}
}
В-пятых, реализация метода экземпляра, а затем цепочка вызовов
метод экземпляраthenСцепленные вызовы имеют два требования:
-
в методе экземпляра
thenМетоды экземпляра можно использовать непосредственно позжеthen. -
в методе предыдущего экземпляра
thenВернуть значение, независимо от того, какое значение, в методе экземпляра позжеthenможно получить в.
На самом деле реализация этого цепного вызова очень проста, в методе экземпляраthenВозвращает новый объект Promise с методом экземпляраthenвозвращаемое значениеvalue,пройти черезresolve(value)илиreject(value)вырубиться.
Сложность реализации этой функции заключается в том, что метод экземпляраthenОценка типа возвращаемого значения и соответствующая обработка. Итак, напишите служебную функциюhandleValueспециально обрабатывать методы экземпляраthenВозвращаемое значение.
then(onFulfilled, onRejected) {
let promise = new Promise((resolve, reject) => {
if (this.status === Fulfilled) {
if (onFulfilled && typeof onFulfilled === 'function') {
setTimeout(() => {
let x = onFulfilled(this.value);
handleValue(promise,x,resolve,reject);
}, 0)
}
}
if (this.status === Rejected) {
if (onRejected && typeof onRejected === 'function') {
setTimeout(() => {
let x = onRejected(this.reason);
handleValue(promise,x,resolve,reject);
}, 0)
}
}
if (this.status === Pending) {
if (onFulfilled && typeof onFulfilled === 'function') {
this.onFulfilled.push(() => {
setTimeout(() => {
let x = onFulfilled(this.value);
handleValue(promise,x,resolve,reject);
}, 0)
})
}
if (onRejected && typeof onRejected === 'function') {
this.onRejected.push(() => {
setTimeout(() => {
let x = onRejected(this.reason);
handleValue(promise,x,resolve,reject);
}, 0)
})
}
}
})
return promise
}
Как и выше, метод экземпляра после преобразованияthenНам удалось получить цепочку под названием. Но еще не реализован метод предыдущего примераthenВозвращает значение, за которым следует метод экземпляраthenдоступно в. следующий вhandleValueреализован в функции.
Возвращаемые значения разных типов согласно спецификации Promise/A+Xправила обработки для достиженияhandleValueфункции, обратите внимание на комментарии кода.
const handleValue = (promise, x, resolve, reject) => {
// 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
if (promise === x) {
return reject(new TypeError('检测到Promise的链式循环引用'))
}
// 确保只传递出去一次值
let once = false;
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
// 防止重复去读取x.then
let then = x.then;
// 判断x是不是Promise
if (typeof then === 'function') {
//调用then实例方法处理Promise执行结果
then.call(x, y => {
if (once) return;
once = true;
// 防止Promise中Promise执行成功后又传递一个Promise过来,
// 要做递归解析。
handleValue(promise, y, resolve, reject);
}, r => {
if (once) return;
once = true;
reject(r);
})
} else {
// 如果x是个普通对象,直接调用resolve(x)
resolve(x);
}
} else {
// 如果x是个原始值,直接调用resolve(x)
resolve(x);
}
}
В приведенном выше кодексе решениеtypeof then === 'function'на самом деле судит о возвращенииxЭто обещание. если нетthenфункция,xЭто нормальное значение, которое возвращается напрямуюresolve(x). Если естьthenфункция,xТо есть обещание, рекурсивно разрешайте обещание до тех пор, покаxявляется обычным значением и возвращается как окончательный результат.
Так зачем использоватьtypeof then === 'function'судитьxэто Promise , вместо использованияx instanceof Promise. Это сделано для того, чтобы сделать Promises более общим, поэтому объект, который можно использовать, также можно рассматривать как Promise. thenable object — это объект, которому принадлежитthenОбъект метода, как показано в следующем коде:
let thenable = {
then: function(resolve, reject){
resolve('执行成功')
}
}
существуетthenable.thenчерез методresolveПередать результат успешного выполнения. Однако объект thenable не создается через новый класс Promise, поэтому его нельзя передать черезx instanceof Promiseчтобы определить, является ли это обещанием.
Также упоминается в спецификации Promise A+ 1.1: Promise — этоthenОбъект или функция методов, поведение которых соответствует этой спецификации.
«обещание» — это объект или функция с методом then, поведение которых соответствует этой спецификации.
6. Реализация метода экземпляра, затем проникновение значения
Метод реализации вышеthenВ процессе реализации цепного вызова передача значения реализована, конечно же, вthenВ сценариях с входящими параметрами.
затем в методе экземпляраthenПараметры не передаются, например:
const test = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('"执行成功"')
}, 3000)
})
test.then().then(res =>{
console.log(res)
})
Метод экземпляра за это времяthenВы все еще можете получить предыдущий метод экземпляраthenВозвращаемое значение, это называется проникновением значения. Как и в приведенном выше примере, консоль по-прежнему может вывести «выполнение успешно».
Просто измените метод экземпляраthenможет быть достигнут.
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };;
let promise = new Promise((resolve, reject) => {
if (this.status === Fulfilled) {
setTimeout(() => {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
}, 0)
}
if (this.status === Rejected) {
if (onRejected && typeof onRejected === 'function') {
setTimeout(() => {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
}, 0)
}
}
if (this.status === Pending) {
this.onFulfilled.push(() => {
setTimeout(() => {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
}, 0)
})
if (onRejected && typeof onRejected === 'function') {
this.onRejected.push(() => {
setTimeout(() => {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
}, 0)
})
}
}
})
return promise
}
7. Обработка ошибок внутри Promise
Приведенный выше код не фиксирует внутреннюю ошибку выполнения Promise, вы можете использоватьtry ... catchоператор для обнаружения ошибок и передачи их с помощью встроенных методовreject, чтобы предотвратить неотслеживаемые ошибки выполнения внутри промиса.
// 在这里用Symbol定义三种状态,防止外部改变状态
const Pending = Symbol('Pending'); // 进行中
const Fulfilled = Symbol('Fulfilled'); // 已成功
const Rejected = Symbol('Rejected'); // 已失败
const handleValue = (promise, x, resolve, reject) => {
// 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
if (promise === x) {
return reject(new TypeError('检测到Promise的链式循环引用'))
}
// 确保只传递出去一次值
let once = false;
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
try {
// 防止重复去读取x.then
let then = x.then;
// 判断x是不是Promise
if (typeof then === 'function') {
//调用then实例方法处理Promise执行结果
then.call(x, y => {
if (once) return;
once = true;
// 防止Promise中Promise执行成功后又传递一个Promise过来,
// 要做递归解析。
handleValue(promise, y, resolve, reject);
}, r => {
if (once) return;
once = true;
reject(r);
})
} else {
// 如果x是个普通对象,直接调用resolve(x)
resolve(x);
}
} catch (err) {
if (once) return;
once = true;
reject(err);
}
} else {
// 如果x是个原始值,直接调用resolve(x)
resolve(x);
}
}
class Promise {
constructor(executor) {
this.status = Pending; //存储 Promise 的状态
this.value = undefined; //存储executor函数中业务代码执行成功的结果
this.reason = undefined; //存储executor函数中业务代码执行失败的原因
this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
const resolve = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Fulfilled;
this.value = value;
// 依次调用成功回调函数
this.onFulfilled.forEach(fn => fn());
}
};
const reject = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Rejected;
this.reason = value;
// 依次调用失败回调函数
this.onRejected.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};;
let promise = new Promise((resolve, reject) => {
if (this.status === Fulfilled) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === Rejected) {
if (onRejected && typeof onRejected === 'function') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
}
}
if (this.status === Pending) {
this.onFulfilled.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
})
if (onRejected && typeof onRejected === 'function') {
this.onRejected.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
})
}
}
})
return promise
}
}
8. Проверьте, соответствует ли обещание спецификации
Существуют специальные тестовые сценарии для проверки того, соответствует ли код, который вы пишете, спецификации PromiseA+.
Установите тестовый скрипт:
npm install -g promises-aplus-tests
Скопируйте следующий код в файл promise.js
// 在这里用Symbol定义三种状态,防止外部改变状态
const Pending = Symbol('Pending'); // 进行中
const Fulfilled = Symbol('Fulfilled'); // 已成功
const Rejected = Symbol('Rejected'); // 已失败
const handleValue = (promise, x, resolve, reject) => {
// 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
if (promise === x) {
return reject(new TypeError('检测到Promise的链式循环引用'))
}
// 确保递归解析中只传递出去一次值
let once = false;
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
try {
// 防止重复去读取x.then
let then = x.then;
// 判断x是不是Promise
if (typeof then === 'function') {
//调用then实例方法处理Promise执行结果
then.call(x, y => {
if (once) return;
once = true;
// 防止Promise中Promise执行成功后又传递一个Promise过来,
// 要做递归解析。
handleValue(promise, y, resolve, reject);
}, r => {
if (once) return;
once = true;
reject(r);
})
} else {
// 如果x是个普通对象,直接调用resolve(x)
resolve(x);
}
} catch (err) {
if (once) return;
once = true;
reject(err);
}
} else {
// 如果x是个原始值,直接调用resolve(x)
resolve(x);
}
}
class Promise {
constructor(executor) {
this.status = Pending; //存储 Promise 的状态
this.value = undefined; //存储executor函数中业务代码执行成功的结果
this.reason = undefined; //存储executor函数中业务代码执行失败的原因
this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
const resolve = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Fulfilled;
this.value = value;
// 依次调用成功回调函数
this.onFulfilled.forEach(fn => fn());
}
};
const reject = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Rejected;
this.reason = value;
// 依次调用失败回调函数
this.onRejected.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};;
let promise = new Promise((resolve, reject) => {
if (this.status === Fulfilled) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === Rejected) {
if (onRejected && typeof onRejected === 'function') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
}
}
if (this.status === Pending) {
this.onFulfilled.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
})
if (onRejected && typeof onRejected === 'function') {
this.onRejected.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
})
}
}
})
return promise
}
static defer(){
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
static deferred(){
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
}
module.exports = Promise;
Затем выполните следующую команду в соответствующем каталоге:
promises-aplus-tests promise.js
Результат выполнения выглядит следующим образом
Все 872 тестовых случая пройдены.
9. Реализация экземплярного метода catch
this.catchто естьthis.then(null, onRejected)псевдоним.
catch(onRejected){
this.then(null, onRejected)
}
10. Реализация статического метода Promise.resolve()
Давайте рассмотримPromise.resolve()Применение.
Promise.resolve()Эффект состоит в том, чтобы превратить объект Promise в переданный параметр.
-
Если параметр является экземпляром Promise, этот экземпляр Promise возвращается напрямую.
-
Если параметр является доступным объектом,
тогда доступные объекты относятся к объектам, которые
thenобъект метода, например.let thenable = { then(resolve,reject){ resolve (42); } }Promise.resolve()Метод превратит этот объект в объект Promise, а затем сразу же выполнит доступный объект.thenметод. -
Параметр не имеет
thenобъект метода или вообще не объект, тоPromise.resolve()Метод возвращает новый экземпляр Promise со статусом «успешно» и передает параметры. -
без каких-либо параметров,
Promise.resolve()Метод позволяет напрямую возвращать новый экземпляр промиса со статусом успешного при вызове без параметров.
Это легко реализовать в соответствии с его использованиемPromise.resolve()
class Promise{
//...
static resolve(param) {
if (param instanceof Promise){
return param;
}
return new Promise((resolve,reject) =>{
if(
param &&
Object.prototype.toString.call(param) === '[object Object]' &&
typeof param.then === 'function'
){
setTimeout(() =>{
param.then(resolve,reject)
},0)
}else{
resolve(param)
}
})
}
}
11. Реализация статического метода Promise.reject()
Давайте рассмотримPromise.reject()Использование: возвращает новый экземпляр Promise со статусом «сбой» и передает аргумент в качестве причины сбоя.
Это легко реализовать в соответствии с его использованиемPromise.reject()
class Promise{
//...
static reject(param){
return new Promise((resolve,reject) =>{
reject(param)
})
}
}
12. Реализация статического метода Promise.all()
Давайте рассмотримPromise.all()Применение.
Promise.all()Роль заключается в том, чтобы обернуть несколько экземпляров Promise в новый экземпляр Promise.
Например:p = Promise.all(p1,p2,p3), где p1, p2 и p3 не являются экземплярами Promise и будут передаваться внутриPromise.resolve()Превратите его в экземпляр Promise. Состояние p определяется p1, p2, p3, разделенными на два случая.
-
Только когда состояния p1, p2 и p3 станут успешными, состояние p станет успешным.Возвращаемое значение этого pl p2 p3 формирует массив и передается функции обратного вызова p.
-
Пока состояние одного из pl p2 p3 становится неудачным, состояние p становится ошибочным, а возвращаемое значение первого состояния pl p2 p3, которое становится неудачным, будет
Функция обратного вызова, переданная p .
class Promise {
//...
static all(promises) {
//将参数promises转为一个真正的数组
promises = Array.from(promises);
return new Promise((resolve, reject) => {
const length = promises.length;
let value = [];
if (length) {
value = Array.apply(null, {
length: length
})
for (let i = 0; i < length; i++) {
Promise.resolve(promises[i]).then(
res => {
value[i] = res;
if (value.length == length) {
resolve(value);
}
},
err => {
reject(err)
return;
}
)
}
} else {
resolve(value)
}
})
}
}
Тринадцать, реализация статического метода Promise.race()
Давайте рассмотримPromise.race()Применение.
Promise.race()Роль заключается в том, чтобы обернуть несколько экземпляров Promise в новый экземпляр Promise.
Например:p = Promise.all(p1,p2,p3), где p1, p2 и p3 не являются экземплярами Promise и будут передаваться внутриPromise.resolve()Превратите его в экземпляр Promise. Состояние p определяется p1, p2 и p3. Пока одно из состояний pl p2 и p3 изменяется, состояние p немедленно изменится соответствующим образом. В это время возвращаемое значение первого состояния изменяется в pl p2 p3 будет передан обратному вызову p.
class Promise {
//...
static race(promises) {
//将参数promises转为一个真正的数组
promises = Array.from(promises);
return new Promise((resolve, reject) => {
const length = promises.length;
if (length) {
for (let i = 0; i < length; i++) {
Promise.resolve(promises[i]).then(
res => {
resolve(res);
return;
},
err => {
reject(err)
return;
}
)
}
} else {
return
}
})
}
}
14. Полный код
// 在这里用Symbol定义三种状态,防止外部改变状态
const Pending = Symbol('Pending'); // 进行中
const Fulfilled = Symbol('Fulfilled'); // 已成功
const Rejected = Symbol('Rejected'); // 已失败
const handleValue = (promise, x, resolve, reject) => {
// 循环引用,自己等待自己完成,会出错,用reject传递出错误原因
if (promise === x) {
return reject(new TypeError('检测到Promise的链式循环引用'))
}
// 确保递归解析中只传递出去一次值
let once = false;
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
try {
// 防止重复去读取x.then
let then = x.then;
// 判断x是不是Promise
if (typeof then === 'function') {
//调用then实例方法处理Promise执行结果
then.call(x, y => {
if (once) return;
once = true;
// 防止Promise中Promise执行成功后又传递一个Promise过来,
// 要做递归解析。
handleValue(promise, y, resolve, reject);
}, r => {
if (once) return;
once = true;
reject(r);
})
} else {
// 如果x是个普通对象,直接调用resolve(x)
resolve(x);
}
} catch (err) {
if (once) return;
once = true;
reject(err);
}
} else {
// 如果x是个原始值,直接调用resolve(x)
resolve(x);
}
}
class Promise {
constructor(executor) {
this.status = Pending; //存储 Promise 的状态
this.value = undefined; //存储executor函数中业务代码执行成功的结果
this.reason = undefined; //存储executor函数中业务代码执行失败的原因
this.onFulfilled = []; //executor函数中业务代码执行成功回调函数的集合
this.onRejected = []; //executor函数中业务代码执行失败回调函数的集合
const resolve = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Fulfilled;
this.value = value;
// 依次调用成功回调函数
this.onFulfilled.forEach(fn => fn());
}
};
const reject = value => {
// 只有当状态为 Pending 才会改变,来保证一旦状态改变就不会再变。
if (this.status === Pending) {
this.status = Rejected;
this.reason = value;
// 依次调用失败回调函数
this.onRejected.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => {
throw err
};;
let promise = new Promise((resolve, reject) => {
if (this.status === Fulfilled) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === Rejected) {
if (onRejected && typeof onRejected === 'function') {
setTimeout(() => {
try {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
}
}
if (this.status === Pending) {
this.onFulfilled.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
})
if (onRejected && typeof onRejected === 'function') {
this.onRejected.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
handleValue(promise, x, resolve, reject);
} catch (error) {
reject(error)
}
}, 0)
})
}
}
})
return promise
}
static resolve(param) {
if (param instanceof Promise){
return param;
}
return new Promise((resolve,reject) =>{
if(
param &&
Object.prototype.toString.call(param) === '[object Object]' &&
typeof param.then === 'function'
){
setTimeout(() =>{
param.then(resolve,reject)
},0)
}else{
resolve(param)
}
})
}
static reject(param){
return new Promise((resolve,reject) =>{
reject(param)
})
}
static all(promises) {
//将参数promises转为一个真正的数组
promises = Array.from(promises);
return new Promise((resolve, reject) => {
const length = promises.length;
let value = [];
if (length) {
value = Array.apply(null, {
length: length
})
for (let i = 0; i < length; i++) {
Promise.resolve(promises[i]).then(
res => {
value[i] = res;
if (value.length == length) {
resolve(value);
}
},
err => {
reject(err)
return;
}
)
}
} else {
resolve(value)
}
})
}
static race(promises) {
//将参数promises转为一个真正的数组
promises = Array.from(promises);
return new Promise((resolve, reject) => {
const length = promises.length;
if (length) {
for (let i = 0; i < length; i++) {
Promise.resolve(promises[i]).then(
res => {
resolve(res);
return;
},
err => {
reject(err)
return;
}
)
}
} else {
return
}
})
}
}