написать впереди
Promise
даES6
Базовая грамматика также является наиболее часто задаваемым вопросом на собеседовании, поэтому необходимо понимать правила ее реализации.Умение писать от руки поможет вам выделиться на собеседовании и предпринять шаги,offer
Твердый шаг
В предыдущей статье я перевелPromise A+
Спецификация, прежде чем читать эту статью, рекомендуется прочитать:【Обещание】Обещания/A+ китайский перевод
Рукописное обещание
Обзор промисов
Promise
это схема управления асинхронным программированием, это конструктор, который доступен для каждого использованияnew
Создайте экземпляр; он имеет три состояния:pending
,fulfilled
иrejected
, эти три состояния не будут затронуты внешним миром, и состояние может быть определено толькоpending
статьfullfilled
(успех),pending
статьrejected
(неудача), а один раз поменяется, больше не изменится.После изменения состояния вернет успешный результат или причину неудачи, выкинетresolve
,reject
,catch
,finally
,then
,all
,race
,done
, в последнем предложении добавленоallSettled
метод, он будет возвращаться независимо от успеха или неудачи. Далее мы реализуем весьPromise
исполнительная функция
Мы знаем, что при создании экземпляра промиса он будет выполнен немедленно.executor
функция,executor
В функцию передаются два параметра,resolve
иreject
,еслиexecutor
ошибка выполнения функции,Promise
Состояние экземпляра меняется наrejected
class MyPromise{
constructor(executor) {
this.status = "pending"; // 初始化状态为pending
this.value = undefined; // 初始化返回的成功的结果或者失败的原因
// 这里是resolve方法,成功后执行,将状态改变为resolved,并且将结果返回
let resolve = result => {
if(this.status !== "pending") return; // 状态一旦改变,就不会再变
this.status = "resolved";
this.value = result;
}
// 这里是reject方法,异常时执行,状态改为rejected,并且将失败的原因返回
let reject = reason => {
if(this.status !== "pending") return;
this.status = "rejected";
this.value = reason;
}
// try、catch捕获异常,如果错误,执行reject方法
try {
executor(resolve, reject)
} catch(err) {
reject(err)
}
}
}
Давайте проверим это сейчасPromise
как это выглядит
let p1 = new MyPromise((resolve, reject) => {
resolve(1);
})
let p2 = new MyPromise((resolve, reject) => {
reject(2);
})
console.log(p1);
console.log(p2);
Как видите, состояние изменилось, а значение внутри — это тоже результат успеха и причина неудачи.then
Метод имеет два параметра, первый параметр выполняется в случае успеха, второй параметр выполняется в случае неудачи.then
Связанный вызов аналогичен массиву и т. д. Он вернетPromise
пример. В случае успеха первыйthen
Успешная функция вnull
, он будет продолжать смотреть вниз до тех пор, пока не перестанетnull
выполнение функции, предыдущийthen
Возвращенный результат напрямую повлияет на следующийthen
Какая функция выполняется успешно или нет, разобравшись с этим, давайте попробуем реализовать ее~
затем метод
then(resolveFn, rejectFn) {
// 如果传入的两个参数不是函数,则直接执行返回结果
let resolveArr = [];
let rejectArr = [];
if(typeof resolveFn !== "function") {
resolveFn = result => {
return result;
}
}
if(typeof rejectFn !== "function") {
rejectFn = reason => {
return MyPromise.reject(reason);
}
}
return new Mypromise((resolve, reject) => {
resolveArr.push(result => {
try {
let x = resolveFn(result);
if(x instanceof MyPromise) {
x.then(resolve, reject)
return;
}
resolve(x);
} catch(err) {
reject(err)
}
})
rejectArr.push(reason => {
try {
let x = rejectFn(reason);
if(x instanceof MyPromise) {
x.then(resolve, reject)
return;
}
resolve(x);
} catch(err) {
reject(err)
}
})
})
}
Разберем код выше
class MyPromise{
constructor(executor) {
this.status = "pending"; // 初始化状态为pending
this.value = undefined; // 初始化返回的成功的结果或者失败的原因
this.resolveArr = []; // 初始化then中成功的方法
this.rejectArr = []; // 初始化then中失败的方法
// 定义change方法,因为我们发现好像resolve和reject方法共同的地方还挺多🤔
let change = (status, value) => {
if(this.status !== "pending") return; // 状态一旦改变,就不会再变
this.status = status;
this.value = value;
// 根据状态判断要执行成功的方法或失败的方法
let fnArr = status === "resolved" ? this.resolveArr : this.rejectArr;
// fnArr中的方法依次执行
fnArr.forEach(item => {
if(typeof item !== "function") return;
item(this. value);
})
}
// 这里是resolve方法,成功后执行,将状态改变为resolved,并且将结果返回
let resolve = result => {
change("resolved", result)
}
// 这里是reject方法,异常时执行,状态改为rejected,并且将失败的原因返回
let reject = reason => {
change("rejected", reason);
}
// try、catch捕获异常,如果错误,执行reject方法
try {
executor(resolve, reject)
} catch(err) {
reject(err)
}
}
then(resolveFn, rejectFn) {
// 如果传入的两个参数不是函数,则直接执行返回结果
if(typeof resolveFn !== "function") {
resolveFn = result => {
return result;
}
}
if(typeof rejectFn !== "function") {
rejectFn = reason => {
return MyPromise.reject(reason);
}
}
return new MyPromise((resolve, reject) => {
this.resolveArr.push(result => {
try {
let x = resolveFn(result); // 获取执行成功方法返回的结果
// 如果x是一个promise实例,则继续调用then方法 ==> then链的实现
if(x instanceof MyPromise) {
x.then(resolve, reject)
return;
}
// 不是promise实例,直接执行成功的方法
resolve(x);
} catch(err) {
reject(err)
}
})
this.rejectArr.push(reason => {
try {
let x = rejectFn(reason);
if(x instanceof MyPromise) {
x.then(resolve, reject)
return;
}
resolve(x);
} catch(err) {
reject(err)
}
})
})
}
}
Давайте посмотрим на эффект
new MyPromise((resolve, reject) => {
resolve(1);
}).then(res => {
console.log(res, 'success');
}, err => {
console.log(err, 'error');
})
В это время возникает проблема.Мы обнаруживаем, что вывода вроде бы нет.Что, если мы внесем небольшое изменение в приведенный выше тестовый пример?
new MyPromise((resolve, reject) => {
setTimeout(_ => {
resolve(1);
}, 0)
}).then(res => {
console.log(res, 'success'); // 1 "success"
}, err => {
console.log(err, 'error');
})
Это потому, что созданиеPromise
Экземпляр выполняется немедленноexecutor
функция, еще не выполненнаяthen
метод, то массив, независимо от успеха или неудачи, пуст. Тогда может у друзей есть сомнения, зачем ты это добавил?setTimeout
Это достаточно хорошо? Это связано с тем, что в механизме очереди событийsetTimeout
Он будет помещен в очередь событий и выполнен после завершения основного потока.then
В методе будет храниться функция успеха или неудачи, так что будь то успешный массив или неудачный массив, в нем уже есть значение, и оно будет полностью выполнено в это время~
Но мы не можем писать при использованииsetTimeout
В качестве решения, поскольку мы инкапсулируем, мы должны решить проблему в инкапсулированной функции, Согласно этой идее, мы также можемresolve
иreject
При выполнении метода оценивается, есть ли значение в массиве, если нет, то можно использоватьsetTimeout
Пусть это задержит выполнение, код выглядит следующим образом~
// 这里是resolve方法,成功后执行,将状态改变为resolved,并且将结果返回
let resolve = result => {
// 如果数组中有值,则立即改变状态
if(this.resolveArr.length > 0) {
change("resolved", result)
}
// 如果没值,则延后改变状态
let timer = setTimeout(_ => {
change("resolved", result)
clearTimeout(timer);
}, 0)
}
// 这里是reject方法,异常时执行,状态改为rejected,并且将失败的原因返回
let reject = reason => {
// 如果数组中有值,则立即改变状态
if(this.rejectArr.length > 0) {
change("rejected", reason);
}
// 如果没值,则延后改变状态
let timer = setTimeout(_ => {
change("rejected", reason);
clearTimeout(timer);
}, 0)
}
Теперь попробуем еще раз
// 1、已经成功了
new MyPromise((resolve, reject) => {
resolve('我成功啦,吼吼吼~~~~');
reject('我都已经成功了,你别想让我失败,哼~~');
}).then(res => {
console.log(res, 'success'); // 我成功啦,吼吼吼~~~~ success
}, err => {
console.log(err, 'error');
})
// 2、先失败了
new MyPromise((resolve, reject) => {
reject('失败了,我好委屈,呜呜呜~~');
resolve('已经失败了~~~');
}).then(res => {
console.log(res, 'success');
}, err => {
console.log(err, 'error'); // 失败了,我好委屈,呜呜呜~~ error
})
// 3、链式调用
new MyPromise((resolve, reject) => {
reject('失败了,我好委屈,呜呜呜~~');
resolve('已经失败了~~~');
}).then(res => {
console.log(res);
}, err => {
console.log(err, 'error'); // 失败了,我好委屈,呜呜呜~~ error
return '我要发奋图强,不会被困难所击倒,我要成功!!!'
}).then(res1 => {
console.log(res1, '经过不懈努力,我终于在第二次成功了~'); // 我要发奋图强,不会被困难所击倒,我要成功!!! 经过不懈努力,我终于在第二次成功了~
}, err1 => {
console.log(err1, '第二次失败');
})
Это отлично решает первый вызов и не будет выполнятьсяthen
методический вопрос. При этом реализованы связанные вызовы. Для связанного вызова я скажу еще несколько слов, На самом деле, является ли это цепным вызовом массива, это потому, что последний возврат - это этот экземпляр.
метод ловли
catch
метод состоит в том, чтобы поймать исключение, которое иthen
Вторая функция обратного вызова метода такая же
catch(rejectFn) {
return this.then(null, rejectFn)
}
метод решения
мы знаем,Promsie
также можно использовать так
let p1 = MyPromise.resolve(1);
console.log(p1);
Мы ожидали чего-то подобного, но теперь точно выдает ошибку:MyPromise.resolve
не метод
Теперь нам нужно инкапсулироватьresolve
метод, мы должны четко понимать, чтоresolve
после,Promise
заключается в поддержке непрерывных цепных вызововthen
, поэтому нам нужно выполнитьresolve
метод, который возвращаетPromise
пример
static resolve(result) {
// 返回新的promise实例,执行promise实例中resolve方法
return new MyPromise(resolve => {
resolve(result)
})
}
метод отклонения
рисунокresolve
Метод тот же, за исключением того, что он получает неисправную функцию
static reject(reason) {
// 返回新的promise实例,执行promise实例中reject方法
return new MyPromise((_, reject) => {
reject(reason);
})
}
готовый метод
ES6
Вводная стандартная книга, даdone
Объяснение метода следующее:Независимо от того, заканчивается ли цепочка обратного вызова объекта Promise методом then или методом catch, пока последний метод выдает ошибку, она может не быть перехвачена. С этой целью Promise предоставляет метод done, который всегда находится в конце цепочки обратного вызова и гарантированно выдает любые возможные ошибки.. Что ж, мы знаем, что делает этот метод, давайте начнем писать прямо сейчас~
done(resolveFn, rejectFn) {
this.then(resolveFn, rejectFn)
.catch(reason => {
setTimeout(() => {
throw reason;
}, 0)
})
}
он может получитьfulfilled
,rejected
Функция обратного вызова состояния или параметры не могут быть предоставлены. Но несмотря ни на что,done
Метод поймает любую возможную ошибку и выдаст ее в глобальную
наконец метод
finally
Метод — это метод, который будет выполняться независимо от успеха или неудачи.Такие методы также есть в апплете.complete
метод и т.д., давайте попробуем его реализовать~
finally(finallyFn) {
let P = this.constructor;
return this.then(
value => P.resolve(finallyFn()).then(() => value),
reason => P.reject(finallyFn()).then(() => reason)
)
}
Давайте проверим
new MyPromise((resolve, reject) => {
reject('失败了,我好委屈,呜呜呜~~');
resolve('已经失败了~~~');
}).then(res => {
console.log(res);
}, err => {
console.log(err, 'error'); // 失败了,我好委屈,呜呜呜~~ error
return '我要发奋图强,不会被困难所击倒,我要成功!!!'
}).finally(() => {
console.log('执行了吗'); // 这里会输出"执行了吗"
})
все методы
all
Метод получает массив и возвращается только тогда, когда каждый экземпляр в массиве успешен, и возвращает массив, и каждый параметр является соответствующимpromise
Результат возвращается, если один терпит неудачу,all
метод возвращает ошибку
// 接收数组参数
static all(promiseList) {
// 返回新实例,调用后还可使用then、catch等方法
return new MyPromise((resolve, reject) => {
let index = 0, // 成功次数计数
results = []; // 返回的结果
for(let i = 0; i < promiseList.length; i++) {
let item = promiseList[i];
// 如果item不是promise实例
if(!(item instanceof MyPromise)) return;
item.then(result => {
index++;
results[i] = result;
if(index === promiseList.length) {
resolve(results);
}
}).catch(reason => {
reject(reason);
})
}
})
}
проверять
// 1.有失败的情况
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.all([p1, p2, p3])
.then(res => {
console.log(res);
}).catch(err => {
console.log(err, 'err'); // 2 "err"
})
// 2.无失败的情况
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.all([p1, p2, p3])
.then(res => {
console.log(res, 'success'); // [1, 2, 3] "success"
}).catch(err => {
console.log(err, 'err');
})
метод гонки
race
Метод также получает параметр массива, каждый из которыхPromise
экземпляр, который возвращает самое быстрое изменение состоянияPromise
результат метода экземпляра
static race(promiseList) {
return new MyPromise((resolve, reject) => {
promiseList.forEach(item => {
if(!(item instanceof MyPromise)) return;
item.then(result => {
resolve(result);
}).catch(err => {
reject(err)
})
})
})
}
проверять
// 1.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
.then(res => {
console.log(res); // 1 'success'
}).catch(err => {
console.log(err, 'err');
})
// 2.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
.then(res => {
console.log(res, 'success');
}).catch(err => {
console.log(err, 'err'); // 1 'err'
})
// 3.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.reject(3);
MyPromise.race([p1, p2, p3])
.then(res => {
console.log(res, 'success');
}).catch(err => {
console.log(err, 'err'); // 1 'err'
})
попробуйте реализовать метод allSettled
allSettled
Метод также принимает параметры массива, но возвращает результат в случае успеха или неудачи.
static allSettled(promiseList) {
return new MyPromise((resolve, reject) => {
let results = [];
for(let i = 0; i < promiseList.length; i++) {
let item = promiseList[i];
if(!(item instanceof MyPromise)) return;
item.then(result => {
results[i] = result;
}, reason => {
results[i] = reason;
})
resolve(results);
}
})
}
проверять
// 1.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.resolve(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
.then(res => {
console.log(res); // [1, 2, 3] 'success'
}).catch(err => {
console.log(err, 'err');
})
// 2.
let p1 = MyPromise.reject(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.reject(3);
MyPromise.race([p1, p2, p3])
.then(res => {
console.log(res, 'success'); // [1, 2, 3] 'success'
}).catch(err => {
console.log(err, 'err');
})
// 3.
let p1 = MyPromise.resolve(1);
let p2 = MyPromise.reject(2);
let p3 = MyPromise.resolve(3);
MyPromise.race([p1, p2, p3])
.then(res => {
console.log(res, 'success'); // [1, 2, 3] 'success'
}).catch(err => {
console.log(err, 'err');
})
Наконец
В этой статье реализован один из собственныхPromise
, исходный код был загружен наgithub
:Исходный адрес MyPromise. Друзья, которым это нужно, могут получить это сами ~
Вы также можете следить за моей официальной учетной записью «веб-дневник», чтобы получать push-сообщения более своевременно ~