предисловие
Промис должен быть знаком всем: асинхронный процесс JavaScript от исходного обратного вызова к промису, генератору и затем к наиболее часто используемому асинхронному/ожиданию (если вы не знакомы с ними, вы можете обратиться к другой моей статье).Асинхронное программирование JavaScript), это не только развитие технической реализации, но и прогресс в том, как управлять асинхронностью в идеологии. Обещания, как основа для последующих планов, являются главным приоритетом и являются наиболее часто задаваемыми вопросами во время интервью.
Сегодня мы реализуем Promise на основе спецификации A+ от 0 до 1. В процессе мы также обсудим обработку исключений Promise и возможность его завершения вручную. Наконец, мы проведем модульное тестирование реализованного Promise. Полный код загружен на github. Если вы хотите увидеть код напрямую, вы можетекликните сюда.
Хотя уже есть много статей, которые помогут вам реализовать класс Promise, у всех разный уровень понимания.Возможно, разные статьи могут натолкнуть вас на разные мысли, так что давайте начнем.
текст
1. Базовая структура
Когда new Promise() получает функцию-исполнитель в качестве параметра, функция будет выполнена немедленно.В функции есть два параметра, которые также являются функциями, а именно: разрешение и отклонение.Синхронное выполнение функции должно быть помещено в попытку. ..catch, иначе перехват ошибок невозможен.
MyPromise.js
function MyPromise(executor) {
function resolve(value) {
}
function reject(reason) {
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
module.exports = MyPromise;
resolve() получает значение значения успеха обещания, а reject получает причину отказа обещания.
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
resolve(123);
})
2. Добавьте конечный автомат
Проблемы с текущей реализацией:
- Обещание — это механизм конечного автомата, начальное состояние
pending
, состояние успехаfulfilled
, состояние отказаrejected
. только отpending
->fulfilled
, или изpending
->rejected
, и как только состояние изменится, оно больше никогда не изменится.
Поэтому нам нужно добавить в Promise механизм потока состояний.
MyPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(executor) {
let self = this;
self.state = PENDING;
function resolve(value) {
if (self.state === PENDING) {
self.state = FULFILLED;
}
}
function reject(reason) {
if (self.state === PENDING) {
self.state = REJECTED;
}
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
module.exports = MyPromise;
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
resolve(123);
});
promise.then(function(value) {
console.log('value', value);
}, function(reason) {
console.log('reason', reason);
})
3. Добавитьthen
метод
Обещания имеютthen
Способ получения двух функцийonFulfilled
а такжеonRejected
, как обратные вызовы для Promise успеха и неудачи соответственно. Итак, вthen
В методе нам нужноstate
судить, еслиfulfilled
, затем выполнитеonFulfilled(value)
метод, еслиrejected
, затем выполнитеonRejected(reason)
метод.
Из-за ценности успехаvalue
и причина отказаreason
пользователемexecutor
прошедшийresolve(value)
а такжеreject(reason)
передано, поэтому нам нужен глобальныйvalue
а такжеreason
доступны для последующих методов.
MyPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(executor) {
let self = this;
self.state = PENDING;
self.value = null;
self.reason = null;
function resolve(value) {
if (self.state === PENDING) {
self.state = FULFILLED;
self.value = value;
}
}
function reject(reason) {
if (self.state === PENDING) {
self.state = REJECTED;
self.reason = reason;
}
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
if (self.state === FULFILLED) {
onFuifilled(self.value);
}
if (self.state === REJECTED) {
onRejected(self.reason);
}
};
module.exports = MyPromise;
4. Реализуйте асинхронные вызовы для разрешения
Проблемы с текущей реализацией:
- Синхронный вызов
resolve()
Нет проблем, но если это асинхронный вызов, например, установкаsetTimeout
, потому что текущий код вызываетthen()
Когда методstate
ещеpending
Статус, вызываемый, когда таймер истекresolve()
Пучокstate
превратиться вfulfilled
статус, ноonFulfilled()
У функции истекло время для вызова.
В ответ на вышеуказанные проблемы вносятся следующие модификации:
MyPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function MyPromise(executor) {
let self = this;
self.state = PENDING;
self.value = null;
self.reason = null;
self.onFulfilledCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value) {
if (self.state === PENDING) {
self.state = FULFILLED;
self.value = value;
self.onFulfilledCallbacks.forEach(function(fulfilledCallback) {
fulfilledCallback();
});
}
}
function reject(reason) {
if (self.state === PENDING) {
self.state = REJECTED;
self.reason = reason;
self.onRejectedCallbacks.forEach(function(rejectedCallback) {
rejectedCallback();
});
}
}
try {
executor(resolve, reject);
} catch (reason) {
reject(reason);
}
}
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
if (self.state === PENDING) {
self.onFulfilledCallbacks.push(() => {
onFuifilled(self.value);
});
self.onRejectedCallbacks.push(() => {
onRejected(self.reason);
});
}
if (self.state === FULFILLED) {
onFuifilled(self.value);
}
if (self.state === REJECTED) {
onRejected(self.reason);
}
};
module.exports = MyPromise;
Добавляем два массива callback-функцийonFulfilledCallbacks
а такжеonRejectedCallbacks
, хранитьthen()
Обратные вызовы успеха и неудачи, переданные в методе. Затем, когда пользователь вызываетresolve()
илиreject()
, изменитьstate
состояние, а callback-функции последовательно вынимаются из соответствующего callback-массива для выполнения.
В то же время, таким образом, мы также можем зарегистрировать несколькоthen()
функций и выполняются в порядке регистрации при успехе или неудаче.
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
setTimeout(function() {
resolve(123);
}, 1000);
});
promise.then(function(value) {
console.log('value1', value);
}, function(reason) {
console.log('reason1', reason);
});
promise.then(function(value) {
console.log('value2', value);
}, function(reason) {
console.log('reason2', reason);
});
5. затем возвращает обещание
Учащиеся, прочитавшие спецификацию PromiseA+, должны знать, чтоthen()
Метод возвращает все еще обещание и возвращает обещаниеresolve
Значение предыдущего промисаonFulfilled()
функция илиonRejected()
Возвращаемое значение функции. Если вы находитесь на предыдущем Обещанииthen()
Если во время выполнения функции обратного вызова метода возникает ошибка, она будет перехвачена и использована в качестве возврата промиса.onRejected
Передаются параметры функции. Например:
let promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((value) => {
console.log('value1', value);
return 456;
}).then((value) => {
console.log('value2', value);
});
let promise = new Promise((resolve, reject) => {
resolve(123);
});
Результат печати:
value1 123
value2 456
let promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((value) => {
console.log('value1', value);
a.b = 2; // 这里存在语法错误
return 456;
}).then((value) => {
console.log('value2', value);
}, (reason) => {
console.log('reason2', reason);
});
Результат печати:
value1 123
reason2 ReferenceError: a is not defined
можно увидеть,then()
Если в функции обратного вызова метода возникает ошибка, она будет перехвачена, а затемthen()
Возвращенный Promise автоматически станетonRejected
,воплощать в жизньonRejected()
Перезвоните.
let promise = new Promise((resolve, reject) => {
reject(123);
});
promise.then((value) => {
console.log('value1', value);
return 456;
}, (reason) => {
console.log('reason1', reason);
return 456;
}).then((value) => {
console.log('value2', value);
}, (reason) => {
console.log('reason2', reason);
});
Результат печати:
reason1 123
value2 456
Ладно, давай дальшеthen()
Метод по-прежнему возвращает обещание.
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
let promise2 = null;
promise2 = new MyPromise((resolve, reject) => {
if (self.state === PENDING) {
self.onFulfilledCallbacks.push(() => {
try {
let x = onFuifilled(self.value);
self.resolvePromise(promise2, x, resolve, reject);
} catch(reason) {
reject(reason);
}
});
self.onRejectedCallbacks.push(() => {
try {
let x = onRejected(self.reason);
self.resolvePromise(promise2, x, resolve, reject);
} catch(reason) {
reject(reason);
}
});
}
if (self.state === FULFILLED) {
try {
let x = onFuifilled(self.value);
self.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}
if (self.state === REJECTED) {
try {
let x = onRejected(self.reason);
self.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}
});
return promise2;
};
Как видите, мы добавилиpromise2
так какthen()
Возвращаемое значение метода. пройти черезlet x = onFuifilled(self.value)
илиlet x = onRejected(self.reason)
получатьthen()
Возвращаемое значение функции обратного вызова метода, а затем вызовself.resolvePromise(promise2, x, resolve, reject)
, добавитpromise2
,x
,promise2
изresolve
а такжеreject
входящий вresolvePromise()
середина.
Итак, сосредоточимся наresolvePromise()
метод.
MyPromise.js
MyPromise.prototype.resolvePromise = function(promise2, x, resolve, reject) {
let self = this;
let called = false; // called 防止多次调用
if (promise2 === x) {
return reject(new TypeError('循环引用'));
}
if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) {
// x是对象或者函数
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, (y) => {
// 别人的Promise的then方法可能设置了getter等,使用called防止多次调用then方法
if (called) return ;
called = true;
// 成功值y有可能还是promise或者是具有then方法等,再次resolvePromise,直到成功值为基本类型或者非thenable
self.resolvePromise(promise2, y, resolve, reject);
}, (reason) => {
if (called) return ;
called = true;
reject(reason);
});
} else {
if (called) return ;
called = true;
resolve(x);
}
} catch (reason) {
if (called) return ;
called = true;
reject(reason);
}
} else {
// x是普通值,直接resolve
resolve(x);
}
};
resolvePromise()
используется для разбораthen()
То, что возвращается в функции обратного вызова, по-прежнему являетсяPromise
,этоPromise
Это может быть наша собственная библиотека, она может быть реализована другими библиотеками илиthen()
объект метода, поэтому здесьresolvePromise()
для единой обработки.
Далее переводится сСпецификация PromiseA+оresolvePromise()
требования:
Процесс разрешения обещаний
Разрешение промисов — это абстрактная операция, которая принимает обещание и значение, которое мы обозначаем как [[Resolve]](promise, x), если x имеет метод then и выглядит как обещание, преобразователь пытается выполнить обещание. состояние x; в противном случае выполняется обещание со значением x.
Эта функция thenable делает реализацию Promise более общей: поскольку она предоставляет метод then, соответствующий протоколу Promise/A+, она также делает реализацию, соответствующую спецификации Promise/A+, совместимой с менее стандартизированными, но пригодными для использования. хорошее сосуществование.
Запуск [[Resolve]](promise, x) выполняется следующим образом:
-
x равно обещанию
Если обещание и x указывают на один и тот же объект, отклонить обещание с помощью TypeError -
х обещание
Если x является Promise , заставить promise принять состояние x :- Если x находится в состоянии ожидания, обещание должно оставаться в ожидании до тех пор, пока x не будет выполнено или отклонено.
- Если x выполняется, выполнить обещание с тем же значением
- Если x находится в отклоненном состоянии, отклонить обещание по той же причине
-
x является объектом или функцией Если x является объектом или функцией:
- присвоить x.then затем
- Если принять значение x.then, выдается ошибка e , отклонить обещание на основании e
- Если then это функция, вызовите ее с x в качестве области видимости функции this . Передайте две функции обратного вызова в качестве параметров, первый параметр называется resolvePromise, а второй параметр называется rejectPromise:
- Если resolvePromise вызывается со значением y, запустите [[Resolve]](promise, y)
- Если rejectPromise вызывается с причиной r в качестве аргумента, отклонить обещание с причиной r
- Если вызываются и resolvePromise, и rejectPromise, или несколько раз с одним и тем же параметром, первый вызов будет иметь приоритет, а остальные будут проигнорированы.
- При вызове метод выдает исключение e:
- Если уже были вызваны resolvePromise или rejectPromise, игнорируйте их.
- В противном случае, согласно е отказывается обещать
- Если then не является функцией, выполнить обещание с x в качестве аргумента
- Если x не является объектом или функцией, выполнить обещание с x в качестве аргумента
Если обещание разрешается объектом в зацикленной цепочке thenable, а рекурсивная природа [[Resolve]](promise, thenable) заставляет его вызываться снова, описанный выше алгоритм попадет в бесконечную рекурсию. Хотя это и не требуется алгоритмом, донорам рекомендуется обнаруживать существование такой рекурсии и, если это так, отклонить обещание с идентифицируемой ошибкой типа.
Обратитесь к приведенным выше спецификациям в сочетании с комментариями в коде, я думаю, каждый может понятьresolvePromise()
эффект.
тестовое задание:
test.js
let MyPromise = require('./MyPromise.js');
let promise = new MyPromise(function(resolve, reject) {
setTimeout(function() {
resolve(123);
}, 1000);
});
promise.then((value) => {
console.log('value1', value);
return new MyPromise((resolve, reject) => {
resolve(456);
}).then((value) => {
return new MyPromise((resolve, reject) => {
resolve(789);
})
});
}, (reason) => {
console.log('reason1', reason);
}).then((value) => {
console.log('value2', value);
}, (reason) => {
console.log('reason2', reason);
});
распечатать результат:
value1 123
value2 789
6. Пустьthen()
Обратные вызовы методов всегда вызываются асинхронно
официальныйPromise
Реализованные функции обратного вызова всегда вызываются асинхронно:
console.log('start');
let promise = new Promise((resolve, reject) => {
console.log('step-');
resolve(123);
});
promise.then((value) => {
console.log('step--');
console.log('value', value);
});
console.log('end');
распечатать результат:
start
step-
end
step--
value1 123
Промисы — это микрозадачи, здесь для удобства мы используем макрозадачи.setTiemout
Вместо реализации асинхронности вы можете обратиться к другой моей статье о макрозадачах, микрозадачах и цикле событий.Позвольте вам полностью понять цикл событий.
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) {
let self = this;
let promise2 = null;
promise2 = new MyPromise((resolve, reject) => {
if (self.state === PENDING) {
self.onFulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFuifilled(self.value);
self.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
});
self.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(self.reason);
self.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
});
}
if (self.state === FULFILLED) {
setTimeout(() => {
try {
let x = onFuifilled(self.value);
self.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
}
if (self.state === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(self.reason);
self.resolvePromise(promise2, x, resolve, reject);
} catch (reason) {
reject(reason);
}
}, 0);
}
});
return promise2;
};
тестовое задание:
test.js
let MyPromise = require('./MyPromise.js');
console.log('start');
let promise = new MyPromise((resolve, reject) => {
console.log('step-');
setTimeout(() => {
resolve(123);
}, 1000);
});
promise.then((value) => {
console.log('step--');
console.log('value', value);
});
console.log('end');
распечатать результат:
start
step-
end
step--
value1 123
После вышеописанных шагов был реализован базовый промис, далее мы реализуем некоторые методы расширения, которых нет в спецификации PromiseA+.
7. Реализацияcatch()
метод
then()
методonFulfilled
а такжеonRejected
Функции обратного вызова передавать не требуется, если не передать, то мы не сможем получитьreject(reason)
ошибка в , то мы можем позвонитьcatch()
метод получения ошибок. Пример:
let promise = new Promise((resolve, reject) => {
reject('has error');
});
promise.then((value) => {
console.log('value', value);
}).catch((reason) => {
console.log('reason', reason);
});
распечатать результат:
reason has error
Не только это,catch()
Его можно использовать в качестве последнего шага вызова цепочки промисов, и ошибка, возникшая в предыдущем промисе, будет всплывать до последнего.catch()
чтобы поймать исключение. Пример:
let promise = new Promise((resolve, reject) => {
resolve(123);
});
promise.then((value) => {
console.log('value', value);
return new Promise((resolve, reject) => {
reject('has error1');
});
}).then((value) => {
console.log('value', value);
return new Promise((resolve, reject) => {
reject('has error2');
});
}).catch((reason) => {
console.log('reason', reason);
});
распечатать результат:
value 123
reason has error1
Такcatch()
Как реализуется метод?
Ответ в реализации Promise,onFulfilled
а такжеonRejected
Функции имеют значения по умолчанию:
MyPromise.js
MyPromise.prototype.then = function(onFuifilled, onRejected) {
onFuifilled = typeof onFuifilled === 'function' ? onFuifilled : value => {return value;};
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
};
MyPromise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
};
можно увидеть,onRejected
Значение по умолчанию — поставить ошибкуreason
пройти черезthrow
выбросить. Поскольку мы выполняем синхронный код вtry...catch
, поэтому, если обещание не выполнено, если оно не переданоonRejected
, функция по умолчанию выдаст ошибкуreason
throws, который затем перехватывается promise2, какreject(reason)
разрешающая способность.
catch()
Чтобы реализовать, нужно позвонитьthis.then(null, onRejected)
,из-заpromise2
одеялоreject
, поэтому выполнитonRejected
Обратный вызов, чтобы поймать ошибку первого промиса.
В заключение,then()
метод не прошелonRejected
Перезвоните,Promise
Внутри это поможет вам написать функцию как обратный вызов по умолчанию, функцияthrow
бросатьreject
илиtry...catch
к ошибке, затем ошибкаreason
Будетpromise2
так какreject(reason)
прими решение, и оно будет следующимthen()
методonRejected
Функция обратного вызова вызывается, аcatch
только что написал спец.then(null, onRejected)
Вот и все.
Итак, мы пишемPromise
Когда цепной вызов , вthen()
нельзя пройти вonRejected
Обратный вызов, просто добавьте один в конце цепочки вызововcatch()
Вот именно, чтобы в цепочкеPromise
Возникающие ошибки будутcatch
захвачен.
Пример 1:
let promise = new Promise((resolve, reject) => {
reject(123);
});
promise.then((value) => {
// 注意,不会走这里,因为第一个promise是被reject的
console.log('value1', value);
return new Promise((resolve, reject) => {
reject('has error1');
});
}).then((value) => {
console.log('value2', value);
return new Promise((resolve, reject) => {
reject('has error2');
});
}, (reason) => {
// 注意,这个then有onRejected回调
console.log('reason2', reason);
}).catch((reason) => {
// 错误在上一个then就被捕获了,所以不会走到这里
console.log('reason3', reason);
});
распечатать результат:
reason2 123
Пример 2:
let promise = new Promise((resolve, reject) => {
reject(123);
});
promise.then((value) => {
console.log('value1', value);
return new Promise((resolve, reject) => {
reject('has error1');
});
}).then((value) => {
console.log('value2', value);
return new Promise((resolve, reject) => {
reject('has error2');
});
}).catch((reason) => {
// 由于链条中的then都没有onRejected回调,所以会一直被冒泡到最后的catch这里
console.log('reason3', reason);
});
catch
а такжеthen
вернуть новыйPromise
. У некоторых учащихся могут возникнуть вопросы, еслиcatch
Что делать, если возникла ошибка при выполнении обратного вызова в
распечатать результат:
reason3 123
8. Реализацияfinally
метод
finally
какая-то библиотечная параPromise
Метод расширения, который реализует либоresolve
ещеreject
, Пойдуfinally
метод.
MyPromise.js
MyPromise.prototype.finally = function(fn) {
return this.then(value => {
fn();
return value;
}, reason => {
fn();
throw reason;
});
};
9. Реализацияdone
метод
done
метод какPromise
Последний шаг цепного вызова, используемый для передачи в глобальнуюPromise
Внутренняя обнаруженная ошибка, которая больше не возвращаетPromise
. Обычно используется для окончанияPromise
цепь.
MyPromise.js
MyPromise.prototype.done = function() {
this.catch(reason => {
console.log('done', reason);
throw reason;
});
};
10. РеализацияPromise.all
метод
Promise.all()
получить один, содержащий несколькоPromise
массив, когда всеPromise
обеfulfilled
состояние, возвращает массив результатов, порядок результатов в массиве и переданныеPromise
Заказ один на один. если естьPromise
дляrejected
государство, весьPromise.all
дляrejected
.
MyPromise.js
MyPromise.all = function(promiseArr) {
return new MyPromise((resolve, reject) => {
let result = [];
promiseArr.forEach((promise, index) => {
promise.then((value) => {
result[index] = value;
if (result.length === promiseArr.length) {
resolve(result);
}
}, reject);
});
});
};
test.js
let MyPromise = require('./MyPromise.js');
let promise1 = new MyPromise((resolve, reject) => {
console.log('aaaa');
setTimeout(() => {
resolve(1111);
console.log(1111);
}, 1000);
});
let promise2 = new MyPromise((resolve, reject) => {
console.log('bbbb');
setTimeout(() => {
reject(2222);
console.log(2222);
}, 2000);
});
let promise3 = new MyPromise((resolve, reject) => {
console.log('cccc');
setTimeout(() => {
resolve(3333);
console.log(3333);
}, 3000);
});
Promise.all([promise1, promise2, promise3]).then((value) => {
console.log('all value', value);
}, (reason) => {
console.log('all reason', reason);
})
распечатать результат:
aaaa
bbbb
cccc
1111
2222
all reason 2222
3333
11. РеализацияPromise.race
метод
Promise.race()
получить один, содержащий несколькоPromise
массив , когда естьPromise
дляfulfilled
состояние, когда весь большойPromise
дляonfulfilled
и выполнитьonFulfilled
Перезвоните. если естьPromise
дляrejected
государство, весьPromise.race
дляrejected
.
MyPromise.js
MyPromise.race = function(promiseArr) {
return new MyPromise((resolve, reject) => {
promiseArr.forEach(promise => {
promise.then((value) => {
resolve(value);
}, reject);
});
});
};
test.js
let MyPromise = require('./MyPromise.js');
let promise1 = new MyPromise((resolve, reject) => {
console.log('aaaa');
setTimeout(() => {
resolve(1111);
console.log(1111);
}, 1000);
});
let promise2 = new MyPromise((resolve, reject) => {
console.log('bbbb');
setTimeout(() => {
reject(2222);
console.log(2222);
}, 2000);
});
let promise3 = new MyPromise((resolve, reject) => {
console.log('cccc');
setTimeout(() => {
resolve(3333);
console.log(3333);
}, 3000);
});
Promise.race([promise1, promise2, promise3]).then((value) => {
console.log('all value', value);
}, (reason) => {
console.log('all reason', reason);
})
распечатать результат:
aaaa
bbbb
cccc
1111
all reason 1111
2222
3333
12. РеализацияPromise.resolve
метод
Promise.resolve
создатьfulfilled
идеальноPromise
, как правило, размещается во всемPromise
начало цепочки, используемое для запускаPromise
цепь.
MyPromise.js
MyPromise.resolve = function(value) {
let promise;
promise = new MyPromise((resolve, reject) => {
this.prototype.resolvePromise(promise, value, resolve, reject);
});
return promise;
};
test.js
let MyPromise = require('./MyPromise.js');
MyPromise.resolve(1111).then((value) => {
console.log('value1', value);
return new MyPromise((resolve, reject) => {
resolve(2222);
})
}).then((value) => {
console.log('value2', value);
})
распечатать результат:
value1 1111
value2 2222
из-за входящегоvalue
Это может быть общее значение, это может бытьthenable
, а возможно и другоеPromise
, так что звонитеresolvePromise
разобрать.
12. РеализацияPromise.reject
метод
Promise.reject
создатьrejected
не удалосьPromise
.
MyPromise.js
MyPromise.reject = function(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
};
test.js
let MyPromise = require('./MyPromise.js');
MyPromise.reject(1111).then((value) => {
console.log('value1', value);
return new MyPromise((resolve, reject) => {
resolve(2222);
})
}).then((value) => {
console.log('value2', value);
}).catch(reason => {
console.log('reason', reason);
});
распечатать результат:
reason 1111
13. РеализацияPromise.deferred
метод
Promise.deferred
Может использоваться для задержки выполненияresolve
а такжеreject
.
MyPromise.js
MyPromise.deferred = function() {
let dfd = {};
dfd.promies = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.rfeject = reject;
});
return dfd;
};
Таким образом, вы можете вызывать извнеdfd.resolve()
а такжеdfd.reject()
решатьPromise
.
13. Как остановить одногоPromise
цепь
Предположим такой сценарий, у нас есть очень длинныйPromise
цепные вызовы, этиPromise
является последовательно зависимой связью, если одна из цепочекPromise
Если есть ошибка, нет необходимости выполнять вниз, по умолчанию мы не можем выполнить это требование, потому чтоPromise
Будь тоthen
ещеcatch
вернетPromise
, будет продолжать выполняться внизthen
илиcatch
. Пример:
new Promise(function(resolve, reject) {
resolve(1111)
}).then(function(value) {
// "ERROR!!!"
}).catch()
.then()
.then()
.catch()
.then()
Есть ли способ остановить этот цепной вызов после ОШИБКИ!!!, вообще не выполняя все функции обратного вызова после цепного вызова?
Мы инкапсулируем одинPromise.stop
метод.
MyPromise.js
MyPromise.stop = function() {
return new Promise(function() {});
};
stop
возвращает никогда не выполнявшийсяresolve
илиreject
изPromise
, то этоPromise
всегда вpending
состояние, поэтому никогда не выполняйте внизthen
илиcatch
. Итак, мы останавливаемPromise
цепь.
new MyPromise(function(resolve, reject) {
resolve(1111)
}).then(function(value) {
// "ERROR!!!"
MyPromise.stop();
}).catch()
.then()
.then()
.catch()
.then()
Но у этого есть недостаток, заключающийся в том, что все функции обратного вызова после цепного вызова не могут быть собраны сборщиком мусора.
14. Как исправитьPromise
последний вернулся по цепочкеPromise
Произошла ошибка
См. следующий пример:
new Promise(function(resolve) {
resolve(42)
}).then(function(value) {
a.b = 2;
});
Здесь a не существует, поэтому присвоение значения a.b является синтаксической ошибкой,onFulfilled
Функция обратного вызова завернута вtry...catch
выполняется, будет ошибкаcatch
к, но так как нетthen
илиcatch
, эта ошибка не может быть обработана, она будетPromise
Ешьте, ничего необычного, так часто говорятОбещания могут поглощать ошибки.
Итак, как нам справиться с этой ситуацией?
метод первый
Это то, чего мы уже достигли.done()
.
new Promise(function(resolve) {
resolve(42)
}).then(function(value) {
a.b = 2;
}).done();
done()
метод эквивалентенcatch
, но больше не возвращаетсяPromise
да, обратите вниманиеdone()
В методе не должно быть синтаксической ошибки, иначе его невозможно поймать.
Способ второй
прослушиватель общих ошибокwindow
изerror
События можно зафиксировать
window.addEventListener('error', error => {
console.log(error); // 不会触发
});
Обещания неonRejected()
Ошибки обработки необходимо отслеживатьunhandledrejection
мероприятие
window.addEventListener('unhandledrejection', error => {
console.log('unhandledrejection', error); // 可以触发,而且还可以直接拿到 promise 对象
});
14. Модульное тестирование
конец
Соответствующие модульные тесты и полный код можно найти в моемgithubПосмотри, если тебе поможет, приходи на звезду~