Прелюдия
Во фронтенд-команде компании автора были проблемы с онлайном из-за неправильного использования Promise. Отправной точкой этой статьи является не только понимание промиса, но и написание промиса с функциями. Таким образом, предпосылка заключается в том, что вы освоили основные функции Promise, если вы не прикасались к нему, пожалуйста,Нажмите, чтобы узнать
текст
Знакомо с обещанием особенностью ES6.
1. Обзор функций
-
цепные вызовы.
-
Три внутренних состояния.
Три внутренних состояния: ожидание, выполнение и отклонение, а состояние инициализации — ожидание. Изменения состояния могут быть переведены из ожидающих в выполненные или отклоненные. Никаких других способов конвертации.
2. Фон, который появляется
Решить Ajax обратный черт ада.
ад обратного вызова ajax, неконтролируемая природа заключается в асинхронной или синхронной форме. Такие как:
ajax1({...,success(res1){
...
}})
ajax2({...,params:{res1},success(res2){ // error no res1
...
}})
Когда нашему ajax2 нужно использовать ajax1, мы должны использовать вложенные:
ajax1({...,success(res1){
ajax2({...,params:{res1},success(res2){
/// doing something
}})
}})
这种写法的最大问题就是当嵌套层数很多当时候,代码会变得难以维护。
p1 = function() {
return new Promise(function(resolve){
ajax1({...,success(res1){
resolve(res1)
}})
})
}
p2 = function(){
return new Promise(function(resolve){
ajax2({...,params:{res1},success(res2){
/// doing something
}})
})
}
p1().then(res=>{
return p2()
}).then(res2=>{
// doing something
})
3 Особые характеристики.
- 1. Разобраться в функциях, первый официальный Promise A+,нажмите
- 2. Затем есть использование, обещание es6 Руана Ифэна особенно подробно.нажмите
Здесь следующие несколько определенных функций согласованы в соответствии с Promise A+.
1.new Promise().then(), переданные функции вызываются onFulfilled и onRejected соответственно
начать писать
1. Обещание — это функция
Итак, первое, что мы делаем, это передаем функцию
function Promise(executor) {
if( !isFunc(executor) ){
throw 'Promise2 传递的参数不为functon!!!';
}
}
2. Начальное состояние обещания.
Обещание имеет три состояния, которые можно перечислить с помощью ts
enum pStatus {
pending = 'pending',
fulled = 'fullfilled',
rejected = 'rejected'
}
Затем нам нужно определить некоторые свойства.
function Promise() {
if( !isFunc(executor) ){
throw 'Promise2 传递的参数不为functon!!!';
}
this.status = pStatus.pending; // 默认状态
this.resovlecbs = []; // 回调的resolve函数 主要来自于Promise.prototype.then
this.rejectcbs = []; // 回调的reject函数 主要来自于Promise.prototype.then
this.value; // 记录的resolve值
this.error; // 记录的reject值
}
Мы знаем, что функция, переданная Promise, будет выполняться непосредственно в основном потоке, поэтому нам нужно выполнить ее напрямую.
function Promise() {
if( !isFunc(executor) ){
throw 'Promise2 传递的参数不为functon!!!';
}
this.status = pStatus.pending; // 默认状态
this.resovlecbs = []; // 回调的resolve函数 主要来自于Promise.prototype.then
this.rejectcbs = []; // 回调的reject函数 主要来自于Promise.prototype.then
this.value; // 记录的resolve值
this.error; // 记录的reject值
try {
executor(resolve,reject); // 传递的函数的执行。
} catch (error) {
reject(error); // 捕获的异常会直接执行reject。
}
}
3. Запустить цепную структуру Promise, добиться асинхронных результатов и стать синхронными, а также разрешить ад обратных вызовов.
Первое, что необходимо объяснить, это то, что принцип цепной структуры заключается в постоянном возврате нового промиса, то есть результатом этого является
Promise.prototype.then = function() {
return new Promise(function(resolve,reject){
xxx...
})
}
Прежде всего, нам нужно прояснить, каковы специфические характеристики Promise?
1. Затем переданные значения являются обратным вызовом разрешения и обратным вызовом состояния отклонения соответственно.
2. Передайте значение и передайте значение предыдущего, а затем полностью вниз.
3. Встретьтесь со слоем «первым пришел, первым обслужен», различные стратегии верхнего уровня должны выполняться в первую очередь.
Чтобы понять подробное значение третьей функции, давайте рассмотрим пример:
var p1 = new Promise(function(resolve,reject){
resolve('p1')
});
var p2 = new Promise(function(resolve,reject){
resolve('p2')
});
p1.then(()=>{
console.log('p11')
}).then(()=>{
console.log('p12')
})
p2.then(()=>{
console.log('p21')
}).then(()=>{
console.log('p22')
})
Я думаю, все знают, что последовательность p11 => p21 => p12 => p22. Если причина связана с особенностями макро- и микрозадач, обратитесь к этой статье,Нажмите, чтобы узнать. Вы должны были понять третий пункт.
那么如何实现?
Шаг за шагом:
1) 传递给Promise的函数,完成后我们才会执行then传递的函数。 то есть
new Promise(function(resolve,reject){
resolve('xxx') // (1)只有执行完这个才会执行后面then的 onFulfilled函数
}).then(function(){
...xxx // (2)这是第二步
})
var p = new Promise(...);
p.then(function(){ ... },...);
p.then(function(){ ... },...);
p.then(function(){ ... },...);
Этот вид не-ссылки на самом деле сохраняется onfullfilled в Promise, поэтому вам нужен массив.
Затем есть внутренняя часть обещания для функции разрешения и функции отклонения. Эти две функции передаются в качестве аргументов функции, переданной пользователем. Суть в обходе элементов функций, которые выполняют reslovecbs. И менять состояние, и записывать поступающие значения, эти значения будут передаваться в onFullfilled, а onFullfilled будет решать, продолжать ли проходить дальше. То есть:
then(function onFullfilled(value){ // value 来自于resolve传递的参数。
return value // return 则表示续传 下一个then是否能拿到。
})
Добавим примерно так:
function Promise() {
if( !isFunc(executor) ){
throw 'Promise2 传递的参数不为functon!!!';
}
this.status = pStatus.pending; // 默认状态
this.resovlecbs = []; // 回调的resolve函数 主要来自于Promise.prototype.then
this.rejectcbs = []; // 回调的reject函数 主要来自于Promise.prototype.then
this.value; // 记录的resolve值
this.error; // 记录的reject值
const resolve = (value:object)=>{ // resolve做的三件事
this.value = value; // 记录值 then 的 onFullfilled会用它
this.resovlecbs.forEach((item:Function)=>{
item(value); // 这个就是 onFullfilled函数,会用上面的value
})
this.status = pStatus.fulled; // 把状态改变为 fullfilled
}
// ... reject同理
try {
executor(resolve,reject);
} catch (error) {
reject(error);
}
}
Что выполняется в разрешении, так это функция, хранящаяся в массиве resolveCbs. И эти функции выталкиваются оттуда.但是值得注意的是,函数除了执行then传递的onFullfiled函数和onRejected函数,还要将这两个返回的值,传递下去,所以要执行下一个Promise的resolve
Поскольку первая характеристика разрешается записано значения.
Так что тогда это.
Promise.prototype.then = function (onFullfilled:Function=noop,onRejected:Function=noop) {
let scope = this;
return new Promise(function(resolve = noop,reject = noop){
scope.resovlecbs.push((value)=>{
handlerRes(onFullfilled,value,resolve);
})
scope.rejectcbs.push((error)=>{
handlerRes(onRejected,error,reject);
})
});
}
export function handlerRes(handler,message,next){
let res
if(isFunc(handler)){
res = handler(message);
}
next(res); // 执行下一个函数的resolve
}
Видно, что переданные затем функции onFullfilled и onRejected помещаются в массив resovlecbs экземпляра и rejectcbs() соответственно для достижения эффекта синхронного выполнения resolve и onFullfilled.
И выполняется не только onRresolved, но и выполняется разрешение следующего промиса.
Этим достигается последовательное выполнение цепочки then.
Для конструктора new Promise() нужно сделать несколько шагов, чтобы создать пустой объект и смонтировать все свойства, которые Promise выполняет внутри этого объекта. То есть все свойства этого.
Эффект от переноса следующий:
Но есть две проблемы с написанным выше:
-
1. Невозможно достичь того, что было сказано раньше
符合同层先来先到,异层必定上层先执行的策略。
, этот эффект, формальная очередь цикла событий. Таким образом, мы можем использовать микрозадачи или макрозадачи. Вот чтобы упростить структуру кода, используя setTimeout для имитации, если вам интересно, вы можете узнать об этой библиотеке npm.asap
,кликните сюда -
2. В настоящее время наша функция then записывается путем прямого помещения функции в массив resolvecbs и ожидания выполнения решения, но этот метод не является хакерским.Если мы выполняем сначала решение, мы выполняем затем. Например:
var p1 = new Promise(function(resolve,reject){
resolve('p1')
});
p1.then(()=>{
console.log('p11')
}).
В этот раз мы сначала выполним resolve, завершим обходное выполнение resolveecbs, а затем пройдемся по сбору resolveecbs. Функции, собранные после имени, никогда не будут выполняться. Итак, мы должны судить о государстве.
взломать письмо:
Promise.prototype.then = function (onFullfilled:Function=noop,onRejected:Function=noop) {
let scope = this;
return new Promise(function(resolve = noop,reject = noop){
if(scope.status === pStatus.pending) { // pending则等待执行
scope.resovlecbs.push((value)=>{
handlerRes(onFullfilled,value,resolve);
})
scope.rejectcbs.push((error)=>{
handlerRes(onRejected,error,reject);
})
} else if(scope.status===pStatus.fulled) { // fullfilled则直接执行
handlerRes(onFullfilled,scope.value,resolve);
} else { // rejectd 直接执行
handlerRes(onRejected,scope.error,reject);
}
});
}
Здесь рассматривается несколько сценариев, но вы думаете, что все закончилось? Давайте посмотрим, что говорит Promise A+.
Перевод:- Если Onfulfulted и обещание1 не работает успешно выполнено, обещание2 должна успешно выполнить и вернуть одинаковое значение.
В чем смысл? Давайте посмотрим на пример:
new Promise(function(resolve){
resolve('test')
}).then().then(value=>{
console.log(value)
})
上述情况,会传递吗?
Ответ положительный.这是Promise A+的标准。
Promise.prototype.then = function (onFullfilled:Function,onRejected:Function) {
let scope = this;
return new Promise(function(resolve = noop,reject = noop){
const resolveHandler = function(value){
if(isFunc(onFullfilled)) {
handlerRes(onFullfilled,value,resolve);
} else {
resolve(value)
}
}
const rejectHanlder = function(error) {
if(isFunc(onRejected)){
handlerRes(onRejected,error,resolve);
} else {
reject(error);
}
}
try {
if(scope.status === pStatus.pending) {
scope.resovlecbs.push((value)=>{
resolveHandler(value)
})
scope.rejectcbs.push((error)=>{
rejectHanlder(error);
})
} else if(scope.status===pStatus.fulled) {
resolveHandler(scope.value);
} else { // rejectd
rejectHanlder(scope.error);
}
} catch (error) {
reject(error);
}
});
}
function handlerRes(handler,message,next){
let res
if(isFunc(handler)){
res = handler(message);
}
next(res); // 执行下一个函数的resolve
}
Что ж, большая часть здесь в основном завершена, однако проблема callback hell до сих пор не решена. Давайте посмотрим, как мы справимся с адом обратных вызовов.
promise1.then(function(){
return promise2
}).then(value=>{
console.log(value) // 应该需要拿到promise2的结果
})
В настоящее время результат обработки нашей функции onFullfilled затем выполняется в функции handlerRes, поэтому мы должны изменить эту функцию, чтобы адаптировать ее к обработке возвращаемого типа промиса.
function handlerRes(handler,message,nextResolve,nextReject,Promise){
let res
if(isFunc(handler)){
res = handler(message);
}
if(res && res instanceof Promise) {
if(res.status===pStatus.pending){
res.then(value=>{
nextResolve(value)
},err=>{
nextReject(err)
})
}
} else {
nextResolve(res);
}
}
Обработка Promise была добавлена выше, это нормально?
Проблемы могут возникнуть, если promise2 также является глубоким обещанием.
Например, promise2.then(value=>{});
значение, если этоpromise
Пример,
На этот раз мы по-прежнему будем решать проблему handlerRes.
Итак, нам нужна рекурсивная обработка, давайте преобразуем ее:
export function deepGet(res,Promise2,nextResolve,nextReject){
if(res && res instanceof Promise2) {
if(res.status===pStatus.pending){
res.then(value=>{
deepGet(value,Promise2,nextResolve,nextReject)
},err=>{
nextReject(err)
})
}
} else {
nextResolve(res);
}
}
export function handlerRes(handler,message,nextResolve,nextReject,Promise2){
let res
if(isFunc(handler)){
res = handler(message);
}
deepGet(res,Promise2,nextResolve,nextReject)
}
Полный пример кода выглядит следующим образом:
Promise2.prototype.then = function (onFullfilled:Function,onRejected:Function) {
let scope = this;
return new Promise2(function(resolve = noop,reject = noop){
const resolveHandler = function(value){
if(isFunc(onFullfilled)) {
handlerRes(onFullfilled,value,resolve,reject,scope.constructor);
} else {
resolve(value)
}
}
const rejectHanlder = function(error) {
if(isFunc(onRejected)){
handlerRes(onRejected,error,resolve,reject,scope.constructor);
} else {
reject(error);
}
}
try {
if(scope.status === pStatus.pending) {
scope.resovlecbs.push((value)=>{
resolveHandler(value)
})
scope.rejectcbs.push((error)=>{
rejectHanlder(error);
})
} else if(scope.status===pStatus.fulled) {
resolveHandler(scope.value);
} else { // rejectd
rejectHanlder(scope.error);
}
} catch (error) {
reject(error);
}
});
}
export function deepGet(res,Promise2,nextResolve,nextReject){
if(res && res instanceof Promise2) {
if(res.status===pStatus.pending){
res.then(value=>{
deepGet(value,Promise2,nextResolve,nextReject)
},err=>{
nextReject(err)
})
}
} else {
nextResolve(res);
}
}
export function handlerRes(handler,message,nextResolve,nextReject,Promise2){
let res
if(isFunc(handler)){
res = handler(message);
}
deepGet(res,Promise2,nextResolve,nextReject)
}
Написание функции then завершено, вернемся к самому промису.
Обещания — это микрозадачи, здесь для удобства, Добавьте интервал задачи макроса в сам промис. То же самое верно и для отказа.
function Promise(executor:any) {
if( !isFunc(executor) ){
throw 'Promise2 传递的参数不为functon!!!';
}
this.status = pStatus.pending;
this.resovlecbs = [];
this.rejectcbs = [];
this.value;
this.error;
const resolve = (value:object)=>{
this.value = value;
setTimeout(()=>{
this.resovlecbs.forEach((item:Function)=>{
item(value);
})
this.status = pStatus.fulled;
},0)
}
const reject = (error:Error)=>{
this.error = error;
setTimeout(()=>{
this.status = pStatus.rejected;
if(this.rejectcbs.length ===0){
throw this.error;
} else {
this.rejectcbs.forEach((item:Function)=>{
item(error);
})
}
},0)
// if(this.rejectcbs.length === 0 ) throw error;
}
try {
executor(resolve,reject);
} catch (error) {
reject(error);
}
}
Однако это еще не окончательный вариант, потому что он не решается, а проблема множественных разрешений будет повторять выполнение resolvecbs. Поэтому содержимое функции разрешения должно выполняться только в состоянии ожидания. Например, кто-то сделал бы так:
new Promise(function(resolve){
reslove('ddd')
resolve('ttt')
}).then(value=>{
console.log(value)
})
Чтобы напечатать только одно значение, мы должны принять решение в функции разрешения только тогда, когда оно ожидает
function Promise(executor:any) {
if( !isFunc(executor) ){
throw 'Promise2 传递的参数不为functon!!!';
}
this.status = pStatus.pending;
this.resovlecbs = [];
this.rejectcbs = [];
this.value;
this.error;
const resolve = (value:object)=>{
setTimeout(()=>{
if(this.status===pStatus.pending){ // 避免重复执行。
this.value = value;
this.resovlecbs.forEach((item:Function)=>{
item(value);
})
this.status = pStatus.fulled; // 状态改变
}
},0)
}
const reject = (error:Error)=>{
setTimeout(()=>{ // why
if(this.status===pStatus.pending){ // 添加了判断 避免重复执行
this.error = error;
this.status = pStatus.rejected; //状态改变
if(this.rejectcbs.length ===0){
throw this.error;
} else {
this.rejectcbs.forEach((item:Function)=>{
item(error);
})
}
}
},0)
// if(this.rejectcbs.length === 0 ) throw error;
}
try {
executor(resolve,reject);
} catch (error) {
reject(error);
}
}
Это в основном идеально, но это все еще стандартная проблема обработки Promise A +. Когда значение разрешения (значение) является обещанием, например:
let p1 = new Promise(function(resolve,reject){
resolve('test')
})
new Promise(function(resolve,reject){
resolve(p1)
}).then(value=>{
console.log(value) // 需要打印test。
})
В настоящее время мы вернем экземпляр p1 напрямую в onFullfilled. Продолжайте трансформироваться.
export default function Promise(executor:any) {
if( !isFunc(executor) ){
throw 'Promise2 传递的参数不为functon!!!';
}
this.status = pStatus.pending;
this.resovlecbs = [];
this.rejectcbs = [];
this.value;
this.error;
const resolve = (value:object)=>{
if( value instanceof Promise) { // 这里直接判断
return value['then'](resolve, reject);
}
setTimeout(()=>{
if(this.status===pStatus.pending){
this.value = value;
this.resovlecbs.forEach((item:Function)=>{
item(value);
})
this.status = pStatus.fulled;
}
},0)
}
const reject = (error:Error)=>{
setTimeout(()=>{ //
if(this.status===pStatus.pending){
this.error = error;
this.status = pStatus.rejected;
if(this.rejectcbs.length ===0){
throw this.error;
} else {
this.rejectcbs.forEach((item:Function)=>{
item(error);
})
}
}
},0)
}
try {
executor(resolve,reject);
} catch (error) {
reject(error);
}
}
4. Улов обещания и, наконец, функции.
catch函数和finally函数其实是语法糖,我们完全可以用then替代的。读者大大们思考下。 .
Код приведен ниже:
Promise.prototype.catch = function(catchcb:Function) {
return this.then(undefined, catchcb); // 本质是then
}
Promise.prototype.finally = function (callback) {
return this.then((value)=>{ // 本质是then
callback();
return value;
},callback);
}
p.then(onResolve,onReject).catch(onCatch).finally(onFinal);
p.then(onResolve,onReject).then(undefined,onCatch).then(onFinal,onFinal);
- 1. То, что передано, является Обещанием,
- 2. Переданный объект можно использовать, например { then: function () {} }
- 3. Передайте значения, которые не являются актуальными
- 4. Ничего не передается.
Следует отметить, что Promise.resolve должен передать обещание. Сочинение автора это
Promise.resolve = function(handler){
if( isObject(handler) && 'constructor' in handler && handler.constructor=== this) { // handler 是 Promise
return handler;
} else if (isObject(handler) && isFunc(handler.then) ){ // thenable
return new this(handler.then.bind(handler));
} else { // 非thenable
return new this(function(resolve){
resolve(handler);
})
}
}
Вы можете увидеть, если это:
В случае 1 верните прямо как есть.
В случае 2 возвращается промис, а затем функция объекта передается в качестве параметра в промис.
В случае 3 разрешайте обработчик напрямую.
6.Promise.reject.
Promise.reject не так хлопотно, как разрешить. Полностью передать переданное значение напрямую.
Promise.reject = function() {
const args = Array.prototype.slice.call(arguments);
return new this((resolve, reject) => reject(args.shift()));
}
7. Обещай.все.
Во-первых, это использование:
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
return new Promise(function(resolve,reject){
setTimeout(()=>{
resolve(id);
},id);
})
});
Promise.all(promises).then(function (posts) {
console.log(posts);
})
Массив передается первым. Во-вторых, все массивы, а во-вторых, все промисы, которые существуют в массиве, выполняются до входа в функцию then of all. Таким образом, маркерный регистратор необходим для записи всех выполненных обещаний в режиме реального времени.
Затем идет входящий массив, который может иметь промис, а может и не быть промисом, поэтому требуется взлом. Определите, существует ли функция then.
Promise.all = function(arr) {
if( !isArray(arr) ){
throw 'all函数 传递的参数不为Array!!!';
}
let args = Array.prototype.slice.call(arr);
let resArr = Array.call(null,Array(arr.length)).map(()=>null); // 记录所有的结果
let handlerNum = 0; // 处理标记
return new this((resolve,reject)=>{
for(let i = 0;i<args.length;i++){
let ifunc = args[i];
if(ifunc && isFunc(ifunc.then) ) { //是否存在then函数。
ifunc.then(value=>{
resArr[i] = value;
handlerNum ++; // 标记添加
if(handlerNum>=arr.length){ // 彻底完成
resolve(resArr) // 完成后的数组
}
},error=>{
reject(error);
});
} else { // 非thenable
resArr[i] = ifunc;
handlerNum ++; // 标记添加
if(handlerNum>=arr.length){ // 彻底完成
resolve(resArr) // 完成后的数组
}
}
}
});
}
8. Обещание.гонка.
Перейдите непосредственно к коду, в качестве результата будет возвращен примерно самый быстрый
Promise2.race = function(arr) {
if( !isArray(arr) ){
throw 'race函数 传递的参数不为Array!!!';
}
let args = Array.prototype.slice.call(arr);
let hasResolve = false;
return new this((resolve,reject)=>{
for(let i = 0;i<args.length;i++){
let ifunc = args[i];
if(ifunc && isFunc(ifunc.then) ) {
ifunc.then(value=>{
!hasResolve && resolve(value)
},error=>{
!hasResolve && reject(error);
});
} else {
hasResolve = true;
!hasResolve && resolve(ifunc)
}
}
})
}
Проанализируйте вопросы от коллег.
Исходный код выглядит следующим образом
let test = function() {
return new Promise((resolve,reject)=>{
reject(new Error('test'))
})
}
Promise.resolve('new').then(res=>{
test().then(res2=>{
...
})
}).catch(err=>{
// use err
console.log(err)
})
Возникшая проблема заключается в том, что ошибка не может быть получена в финальном улове.
В тексте мы уже говорили, что поймает только синтаксический сахар, и значение затем передается по возврату Onfullfulled и возврата OneRected. Проблема в том, что возврат отсутствует тогда.
Суммировать:
-
1) Запишите переданное значение, готовое к передаче.
2) Выполнить затем функцию onFullfilled
3) Изменить статус.
-
Исполнение тогда производится по-разному в зависимости от штата.
-
Catch и finally — это просто синтаксический сахар для then, а finally означает не последнее выполнение, а то, что оно будет выполнено.
-
Promise.resolve может быстро создать промис и должен вернуть промис.
Я хочу выразиться яснее, поэтому содержание слишком длинное. . . .
Если у вас есть какие-либо вопросы, пожалуйста, задавайте их в области комментариев, и я отвечу как можно скорее.