Здесь мы впервые поговорим о концепции Promise:
Что такое обещание?
Промисы — это решение для асинхронного программирования, более разумное и мощное, чем традиционные решения — функции обратного вызова и события. Впервые он был предложен и реализован сообществом, ES6 вписал его в стандарт языка, унифицированное использование и изначально предоставил объекты Promise.
Так как же реализовать обещание, соответствующее спецификации?
См. сводку спецификации promiseA+:
Мы знаем, что у промисов есть три состояния ожидающее переходное состояние выполненный отклоненное состояние отказа
Есть только две возможности изменения состояния обещания.
-
Переходное состояние => Успех штата
-
Переходное состояние => состояние отказа
Процесс необратим и не может быть преобразован друг в друга
Вот картинка, чтобы было легче понять
let promise = new Promise((resolve, reject) => {
//这里放入我们要执行的函数,可能是同步,也可能是异步, 这里我们就来写一个异步的执行
setTimeout(() => {
resolve('hello');
})
})
promise.then(data => {
console.log(data);
}, err => {console.log(err)})
Вышеприведенный код означает, что мы создаем экземпляр промиса и выполняем его асинхронно.Здесь, вызывая метод then, мы успешно получаем результат
Первый шаг — соблюсти грамматику
Наблюдая за использованием нативных промисов, мы можем обнаружить, что функция передается при передаче нового промиса. Эта функция вызывается в спецификации как exector Увидев это, у нас сначала появилась общая идея создать собственный конструктор промисов.
// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
}
Хорошо, первый шаг завершен, фокус закончен, что внутри этого промиса?Вы можете видеть, что исходный Exector передал два параметра, первый параметр сделает статус промиса решённым, то есть Успех, второе выполнение приведет к тому, что функция перейдет в состояние REJECT, то есть не удастся
И оба параметра можно передать после выполнения, мы продолжаем улучшать код Мы инкапсулируем функции этих двух параметров внутри конструктора
// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let self = this;
this.value = undefined;
this.reason = undefined;
// 成功执行
function resolve(value) {
self.value = value;
}
// 失败执行
function reject(reason) {
self.reason = reason;
}
exector(resolve, reject);
}
Здесь возникает проблема. Мы знаем, что процесс выполнения обещания необратим, а разрешение и отклонение не могут быть преобразованы друг в друга. Здесь нам нужно добавить состояние, чтобы определить, находится ли оно в данный момент в ожидающем процессе, и наш исполнитель может сообщить об ошибке напрямую. , нам также нужно разобраться с этим здесь.
// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let self = this;
// 这里我们加入一个状态标识
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
// 成功执行
function resolve(value) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.value = value;
// 这里我们执行之后需要更改状态
self.status = 'resolved';
}
}
// 失败执行
function reject(reason) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.reason = reason;
// 这里我们执行之后需要更改状态
self.status = 'rejected';
}
}
// 这里对异常进行处理
try {
exector(resolve, reject);
} catch(e) {
reject(e)
}
}
Оставим здесь маленькую дырочку, потом вернемся и засыпаем
Ну, Promise в основном такой, он очень простой? Здесь мы сначала реализуем упрощенную версию, а следующие функции будут добавляться постепенно. Не волнуйтесь, продолжайте оглядываться назад.
Вторым шагом является реализация цепного вызова
Как мы можем изменить состояние объекта обещания после нового обещания? Благодаря предыдущему нативному использованию мы узнали, что нам нужно использовать метод then. Метод then определяет функции обратного вызова разрешенного и отклоненного состояний соответственно. Так как же узнать, какую функцию обратного вызова использовать? Разве мы не определили статус только что внутри конструктора? Мы будем использовать его здесь, код выше
// 我们将then方法添加到构造函数的原型上 参数分别为成功和失败的回调
Promise.prototype.then = function(onFulfilled, onRejected) {
// 获取下this
let self = this;
if (this.status === 'resolved') {
onFulfilled(self.value);
}
if (this.status === 'rejected') {
onRejected(self.reason);
}
}
хорошо, теперь мы можем попробовать это сами
let promise = new Promise((resolve, reject) => {
resolve("haha");
})
promise.then(data => {
console.log(data); //输出 haha
}, err=> {
console.log(err);
})
// 多次调用
promise.then(data => {
console.log(data); //输出 haha
}, err=> {
console.log(err);
})
Выше можно заметить, что операция изменения состояния в new Promise синхронная, если асинхронная, то обычно мы сталкиваемся с асинхронными операциями, как решить?
Здесь нам нужно хранить в конструкторе два массива, соответственно сохраняя успешный коллбэк и неудавшийся колбэк Поскольку вы можете это сделать несколько раз, вам нужно поместить эти функции в массив код показывает, как показано ниже:
// 这里我们创建了一个构造函数 参数就是执行器
function Promise(exector) {
// 这里我们将value 成功时候的值 reason失败时候的值放入属性中
let self = this;
// 这里我们加入一个状态标识
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
// 存储then中成功的回调函数
this.onResolvedCallbacks = [];
// 存储then中失败的回调函数
this.onRejectedCallbacks = [];
// 成功执行
function resolve(value) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.value = value;
// 这里我们执行之后需要更改状态
self.status = 'resolved';
// 成功之后遍历then中成功的所有回调函数
self.onResolvedCallbacks.forEach(fn => fn());
}
}
// 失败执行
function reject(reason) {
// 判断是否处于pending状态
if (self.status === 'pending') {
self.reason = reason;
// 这里我们执行之后需要更改状态
self.status = 'rejected';
// 成功之后遍历then中失败的所有回调函数
self.onRejectedCallbacks.forEach(fn => fn());
}
}
// 这里对异常进行处理
try {
exector(resolve, reject);
} catch(e) {
reject(e)
}
}
// then 改造
Promise.prototype.then = function(onFulfilled, onRejected) {
// 获取下this
let self = this;
if (this.status === 'resolved') {
onFulfulled(self.value);
}
if (this.status === 'rejected') {
onRejected(self.reason);
}
// 如果异步执行则位pending状态
if(this.status === 'pending') {
// 保存回调函数
this.onResolvedCallbacks.push(() => {
onFulfilled(self.value);
})
this.onRejectedCallbacks.push(() => {
onRejected(self.reason)
});
}
}
// 这里我们可以再次实验
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if(Math.random() > 0.5) {
resolve('成功');
} else {
reject('失败');
}
})
})
promise.then((data) => {
console.log('success' + data);
}, (err) => {
console.log('err' + err);
})
Как реализовать цепной вызов then
Ключевым моментом является начало здесь. Не пропустите его. С помощью приведенного выше кода мы реализовали упрощенную версию обещания. Упрощенная версия заключается в том, что наш метод then можно вызвать только один раз, а связанный вызов в родном обещании не реализована.
Как реализован цепной вызов?
Здесь нам нужно просмотреть спецификацию promiseA+, которую можно узнать, просмотрев спецификацию и объяснение es6 от Ruan Yifeng.
Метод then возвращает новый экземпляр Promise (обратите внимание, не исходный экземпляр Promise). Следовательно, можно использовать метод цепочки, то есть после метода then вызывается другой метод then.
Здесь мы смотрим на кусок нативного кода промиса
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
В приведенном выше коде используется метод then, по очереди определяющий две функции обратного вызова. После завершения первой функции обратного вызова возвращаемый результат будет передан в качестве параметра второй функции обратного вызова.
С цепочкой вы можете указать набор функций обратного вызова, которые будут вызываться по порядку. В это время предыдущая функция обратного вызова может возвращать объект Promise (то есть происходит асинхронная операция), в это время последняя функция обратного вызова будет ждать изменения состояния объекта Promise перед вызовом.
Кроме того, через нативное обещание мы также можем обнаружить, что когда возвращаемое значение последнего успеха или неудачи является распространенным типом данных, все это приводит к следующему успешному обратному вызову then, и мы можем продолжить преобразование метода then .
Promise.prototype.then = function(onFulfilled, onRejected) {
// 获取下this
let self = this;
// 因为then方法返回的是一个promise,这里我们新建一个promise
let promise2 = new Promise((resolve, reject) => {
if (this.status === 'resolved') {
//获取回调的返回值
try {
// 当执行成功回调的时候 可能会出现异常,那就用这个异常作为promise2的错误的结果
let x = onFulfilled(self.value);
//执行完当前成功回调后返回结果可能是promise
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
}
if (this.status === 'rejected') {
//获取回调的返回值
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
}
// 如果异步执行则位pending状态
if(this.status === 'pending') {
// 保存回调函数
this.onResolvedCallbacks.push(() => {
//获取回调的返回值
try {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
})
this.onRejectedCallbacks.push(() => {
//获取回调的返回值
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
});
}
})
return promise2;
}
Здесь мы рассмотрим изменения в новой функции then. Разбираем ее шаг за шагом. Сначала создаем новый промис и возвращаем его. Вот что мы знаем из нативного документа промиса: Метод then возвращает новый экземпляр Promise (обратите внимание, не исходный экземпляр Promise). Следовательно, можно использовать метод цепочки, то есть после метода then вызывается другой метод then.
Разобравшись здесь продолжаем смотреть на него.Внутренне получаем возвращаемое значение после успеха или неудачи метода then и присваиваем его переменной x.Здесь будет несколько ситуаций.Переменная x может быть нормальным значением или обещание.
Мы определяем функцию resolvePromise для передачи обещания, возвращенного к тому времени, возвращаемого значения этого успеха или неудачи и двух параметров обещания, возвращенного к тому времени, в эту функцию, и делаем некоторые суждения Конкретная реализация выглядит следующим образом:
function resolvePromise(promise2,x,resolve,reject){
// promise2和函数执行后返回的结果是同一个对象
if(promise2 === x){
return reject(new TypeError('Chaining cycle'));
}
// x可能是一个promise 或者是一个普通值
if(x!==null && (typeof x=== 'object' || typeof x === 'function')){
try{
let then = x.then;
// 取对象上的属性 怎么能报异常呢?(这个promise不一定是自己写的 可能是别人写的 有的人会乱写)
// x可能还是一个promise 那么就让这个promise执行即可
// {then:{}}
// 这里的逻辑不单单是自己的 还有别人的 别人的promise 可能既会调用成功 也会调用失败
if(typeof then === 'function'){
then.call(x,y=>{ // 返回promise后的成功结果
// 递归直到解析成普通值为止
// 递归 可能成功后的结果是一个promise 那就要循环的去解析
resolvePromise(promise2,y,resolve,reject);
},err=>{ // promise的失败结果
reject(err);
});
}else{
resolve(x);
}
}catch(e){
reject(e);
}
}else{ // 如果x是一个常量
resolve(x);
}
}
Неважно, что здесь немного запутанно, давайте продолжим анализировать эту реализацию.
затем возвращает обещание?
Первый выбор - войти в функцию, Мы оцениваем, равен ли promise2 x, что эквивалентно оценке того, является ли возвращаемое значение последнего успешным, и возвращаемое значение обратного вызова, который должен попасть в бесконечный цикл, Например:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello');
})
})
let p2 = p.then(data => {
return p2;
})
Этот способ записи попадет в бесконечный цикл, поэтому избегайте этого. Хорошо, давайте продолжим чтение, я только что упомянул, что x может быть общим значением или обещанием, поэтому внутри функции должно быть принято решение, является ли это обещанием или нет, Если возврат — промис, то нужно продолжать выполнять промис, и здесь используется рекурсия. При использовании промисов мы также заметим, что различные библиотеки промисов могут быть смешаны, поэтому тип then оценивается внутренне.
Хорошо, вы тут что-то поняли? Давайте продолжим смотреть вниз. Мы знаем, что внутренние состояния одних и тех же промисов не могут трансформироваться друг в друга. Здесь нам нужно сделать внутреннее суждение.
function resolvePromise(promise2,x,resolve,reject){
// promise2和函数执行后返回的结果是同一个对象
if(promise2 === x){
return reject(new TypeError('Chaining cycle'));
}
let called;
// x可能是一个promise 或者是一个普通值
if(x!==null && (typeof x=== 'object' || typeof x === 'function')){
try{
let then = x.then; // 取对象上的属性 怎么能报异常呢?(这个promise不一定是自己写的 可能是别人写的 有的人会乱写)
// x可能还是一个promise 那么就让这个promise执行即可
// {then:{}}
// 这里的逻辑不单单是自己的 还有别人的 别人的promise 可能既会调用成功 也会调用失败
if(typeof then === 'function'){
then.call(x,y=>{ // 返回promise后的成功结果
// 递归直到解析成普通值为止
if(called) return; // 防止多次调用
called = true;
// 递归 可能成功后的结果是一个promise 那就要循环的去解析
resolvePromise(promise2,y,resolve,reject);
},err=>{ // promise的失败结果
if(called) return;
called = true;
reject(err);
});
}else{
resolve(x);
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{ // 如果x是一个常量
resolve(x);
}
}
Мы добавляем вызываемую переменную для предотвращения взаимного преобразования. Код, написанный здесь, это конец? Конечно, нет.Внимательные студенты обнаружат, что есть и другое использование нативного обещания.
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello');
})
})
p.then().then(data => {
console.log(data);
throw new Error('e');
}).then().then(null, err => {
console.log(err);
})
При таком использовании происходит проникновение значения, когда предыдущая функция then не вызывает обратные вызовы успеха и отказа, значение передается в следующий вызов then.
Как добиться этого на самом деле очень просто. Нам нужно только судить, передается ли успешный или неудачный обратный вызов в каждом вызове then. Если нет обратного вызова, мы продолжаем возвращать значение, переданное для успеха или неудачи последнего раунда. тогда. Мы также узнали, что колбэки метода then выполняются асинхронно, здесь мы просто используем таймер для имитации этого, конечно, внутренняя реализация не так проста. Здесь только как простая реализация
код показывает, как показано ниже
Promise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
onRejected = typeof onRejected === 'function'?onRejected: err=>{throw err}
let self = this;
let promise2;
promise2 = new Promise((resolve, reject) => {
if (self.status === 'resolved') {
setTimeout(()=>{
try {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
if (self.status === 'rejected') {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
}
if (self.status === 'pending') {
self.onResolvedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
self.onRejectedCallbacks.push(() => {
setTimeout(()=>{
try {
let x = onRejected(self.reason);
resolvePromise(promise2,x,resolve,reject);
} catch (e) {
reject(e);
}
},0)
});
}
});
return promise2
}
Отличная работа.
Подождите, есть что-то меньшее, вы издеваетесь, молодой человек, не волнуйтесь. И продолжайте смотреть вниз. Обычно мы используем, конечно, какие-то другие методы обещания, например, поймать все гонки. И может написатьPromise.resove().then()
Promise.reject().then()
Дополнение к обещаниям
Давайте реализуем их один за другим.Давайте сначала посмотрим на успех и неудачу вызова класса Promise непосредственно выше. мы можем написать
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}
Как насчет catch, что эквивалентно прямому вводу обратного вызова с ошибкой следующего then?
Promise.prototype.catch = function(onRejected){
// 默认不写成功
return this.then(null,onRejected);
};
Метод Promise.all используется для переноса нескольких экземпляров Promise в новый экземпляр Promise. Для конкретного использования обратитесь к документации es6, которая не будет подробно использоваться.
// 传入一个promise数组
Promise.all = function(promises){
// 返回执行后的结果
return new Promise((resolve,reject)=>{
let arr = [];
let i = 0;
function processData(index,data){
arr[index] = data;
// 判断是否全部成功
if(++i == promises.length){
resolve(arr);
}
}
for(let i = 0;i<promises.length;i++){
promises[i].then(data=>{ // data是成功的结果
//将每次执行成功后的结果传入函数
processData(i,data);
},reject);
}
})
}
гонка еще проще.
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
for(let i = 0;i<promises.length;i++){
promises[i].then(resolve,reject);
}
})
}
Здесь мы уже реализовали некоторые общие функции промисов, здесь нужно посмотреть несколько раз, чтобы углубить память.