Глубокое понимание промисов

JavaScript Promise

Что такое обещание

  • Преимущества асинхронные операции выражены в потоке синхронных операций, избегая слоев вложенных функций обратного вызова
  • Промисы могут решить проблему асинхронности, нельзя сказать, что промисы сами по себе асинхронны.

Особенности обещания

  • Состояние объекта от внешних воздействий.PromiseОбъект представляет асинхронную работу и имеет три состояния:pending(в ходе выполнения),resolved(успешно) иrejected(не удалось)
  • Как только состояние изменится, оно больше не изменится, вы можете получить этот результат в любое время..PromiseСтатус объекта меняется, всего два возможных: сpendingсталиresolvedИ изpendingсталиrejected
  • Внутри обещания происходит ошибка и не повлияет на выполнение внешней программы.
  • Promise. После создания оно будет выполнено немедленно и не может быть отменено на полпути. Во-вторых, если вы не установите функцию обратного вызова,PromiseВнутренние ошибки брошены, не реагируют на улицу. В-третьих, когда вpendingКогда состояние не знает, где текущий прогресс этапа (начало или близится к завершению)

Применение

Основное использование

СоздайтеPromiseнапример, вы должны передать функцию в качестве параметра

new Promise(() => {});
new Promise(); // 报错

Эта функция может принимать две другие функции, предоставляемые движком JavaScript,resolveа такжеreject.函数作用:

  • resolve--БудуPromiseСостояние объекта переходит изpendingсталиresolved, передать результат асинхронной операции в качестве параметра
  • reject--БудуPromiseСостояние объекта переходит изpendingсталиrejectedОшибки асинхронной операции могут быть в качестве параметра для передачи
let promise = new Promise((resolve, reject) => {
  // do something
  if (true) {
    // 将参数返回,供then方法使用
    resolve("value");
  } else {
    // 将参数返回,供then方法使用
    reject("error");
  }
});

PromiseПосле создания экземпляра вы можете использоватьthenспособы оговариваются отдельноresolvedстатус иrejectedФункция обратного вызова статуса.

promise.then(
  value => {
    // resolved时调用,value为resolve函数返回的参数
    console.log(value);
  },
  err => {
    // rejected时调用,err为reject函数返回的参数
    console.log(err);
  }
);

когдаthenКогда метод имеет только один функциональный параметр, онresolvedСостояние метода обратного вызова

promise.then(value => {
    // 只有状态为resolved时才能调用,如果返回的是rejected状态,则报错 Uncaught (in promise) error
    console.log(value);
  });

Только когда обещание состояния разрешено или отклонено, метод будет вызываться

Промисы выполняются, как только они созданы, ипередачаresolveилиrejectPromise

let promise = new Promise(resolve => {
  console.log("Promise");
  resolve();
  console.log("!!!")
});

promise.then(function() {
  console.log("resolved.");
});
console.log("Hi!");

// Promise
// !!!
// Hi!
// resolved

resolvePromiseПример

const p1 = new Promise((_, reject) => {
  setTimeout(() => reject('error'), 3000);
});

const p2 = new Promise(resolve => {
  setTimeout(() => resolve(p1), 1000);
});

p2.then(
  result => console.log(result),
  error => console.log(error) // error
);

В приведенном выше кодеp1ЯвляетсяPromise, через 3 секунды становитсяrejected.p2состояние меняется через 1 секунду,resolveметод возвращаетp1. из-заp2То, что возвращается, является другим обещанием, в результате чегоp2Собственный статус недействителен,p1государственное решениеp2положение дел. Итак, последнийthenВысказывания все становятся направленными к последнему (p1). Еще через 2 секундыp1сталиrejected, вызывая триггерcatchФункция обратного вызова, указанная методом.

Вышеприведенное является исходным объяснением, мы можем понять его какp2.thenв реальностиp1.then

Внутри промиса возникает ошибка, ошибка будет отклонена

const promise = new Promise(resolve => {
  throw new Error("aa") // 等同于reject(new Error("aa"))
  resolve("resolve");
});
console.log(promise) // Promise {<rejected>: Error: aa ...... }

В методе, то то же самое верно

Promise.resolve()

Использование этого метода может быть любое значение в обещании

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

Promise.resolve()Метод разделен на четыре случая параметра

Параметр является примером обещания

Если аргумент является экземпляром Promise, тоPromise.resolveЭтот экземпляр будет возвращен без изменений и без изменений. Он ведет себя как пустая оболочка.

const promise = new Promise((resolve, reject) => {
  reject("resolve");
});
let p = Promise.resolve(promise);
console.log(p == promise); // true

параметр являетсяthenableобъект

thenableОбъект относится кthenОбъектный метод,Promise.resolve()Метод превратит этот объект в объект Promise, а затем немедленно выполнит его.thenableобъектthen()метод

// thenable对象
let thenable = {
  then(resolve, reject) {
    reject(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(value => {
  console.log("resolved",value);
},err => {
  console.log("rejected",err); // 42
});

В приведенном выше кодеthenableобъектthen()После выполнения метода объектp1Государство становитсяresolved, тем самым немедленно выполняя последнийthen()Функция обратного вызова, указанная методом, вывод 42

параметр не имеетthen()объект метода или вообще не объект

const p = Promise.resolve('Hello');
p.then( s => {
  console.log(s) // Hello
});

без параметров

Promise.resolve()Метод разрешено вызывать без параметров, и он напрямую возвращаетresolvedСостояние объекта обещания

Promise.resolve();
// 相当于
new Promise(resolve => resolve(undefined))

Promise.reject()

Promise.reject()Способ также возвращает новое состояние отклонено примерами обещания

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

Любой параметр будет причиной отклонения контракта, даже не решенное обещание

let promise = Promise.resolve("foo")
let p1 = Promise.reject(promise);
p1.then(value => {
  console.log("resolved",value);
},err => {
  console.log("rejected",err); // Promise {<fulfilled>: 'foo'}
});


Promise.prototype.then()

thenМетод определен в объекте-прототипеPromise.prototypeВыше оба параметра метода then являются необязательными. то метод может вернуть**全新的promise实例**Таким образом, то тогда метод может быть прикован. Что определяет возврат этого метода к обещанию?

// 未传入处理程序
const p1 = Promise.resolve("foo")
let pt1 = p1.then();
console.log(p1); // Promise <resolved>: foo
console.log(pt1); // Promise <resolved>: foo
// 未成功调用then方法
const p2 = Promise.reject("bar")
let pt2 = p2.then(value => {
  console.log(value);
});
  1. использоватьPromise.resolve()обертывание возвращаемого значения

Возвращаемое значение по умолчаниюundefined

let pt3 = p2.then(undefined, value => {
  console.log(value);
});
console.log(pt3);

Promise.prototype.catch()

catch()путь.then(null, rejection)или.then(undefined, rejection)Псевдоним для указания функции обратного вызова при возникновении ошибки.catch()Метод также возвращает объект Promise (такой же, как метод then)

const promise = new Promise((_, reject) => {
  reject("reject");
});

promise
  .then(value => {
  console.log(value);
})
	// 发生错误,或者reject时执行
  .catch(value => {
  console.log(value);
});

Если состояние Promise сталоresolved, то выдавать ошибку недопустимо.

const promise = new Promise(resolve => {
  resolve("resolve");
  throw new Error("fail");
});

promise.then(value => console.log(value));

Все в обещанииОшибка не обработанаЛуковицы до последнего улова

const promise = new Promise(resolve => {
  resolve("resolve");
});
promise
  .then(value => {
  console.log(value);
  throw new Error("fail1");
})
  .then(() => {
  throw new Error("fail2");
})
  .catch(value => {
  console.log(value);
});

В приведенном выше коде catch сначала напечатает первую ошибку, а когда первая ошибка будет устранена (закомментировано все в порядке), вторая ошибка будет напечатана в catch.
Возвращаемое значение catch по-прежнему обещано, способ возврата обещания похож на then, поэтому метод then все еще можно вызывать после catch

Ошибки внутри промиса не влияют на код вне промиса

Promise.prototype.finally()

finally()Независимо от метода, используемого для целенаправленного конечного состояния обещания, будет выполнена операция.
finallyФункция обратного вызова метода не принимает никаких параметров, а это значит, чтоfinallyОперации в методе должны быть независимы от состояния и не зависеть от результата выполнения промиса.

const promise = new Promise(resolve => {
  resolve("resolve");
});
promise.finally(() => {
  console.log(11); // 11
});

finallyсущность

promise.finally(() => {
  // do something
});

// 等同于
promise.then(
  result => {
    // do something
    return result;
  },
  error => {
    // do something
    throw error;
  }
);

finallyВозвращаемое значение — это новое обещание, похожее на исходное значение.

Promise.all() 

Promise.all()Метод для множества экземпляров обещают, упакованные в новое обещание экземпляра

const p = Promise.all([p1, p2, p3]);

Promise.all()p1,p2,p3оба экземпляра Promise, если нет, то он будет вызванPromise.resolve方法,将参数转为 Promise 实例,再进一步处理。 Кроме того,Promise.all()Параметры метода могут не быть массивами, но у них должен быть интерфейс итератор, и каждый возвращенный элемент - это экземпляр обещания.
pГосударствомp1,p2,p3Решение разделено на два случая.

  • Толькоp1,p2,p3Государство сталоresolved,pгосударство станетresolved,В настоящее времяp1,p2,p3Возвращаемые значения образуют массив, который передается вpфункция обратного вызова.
  • если толькоp1,p2,p3один из них былrejected,pГосударство становитсяrejectedВ этом случае первымrejectПримеры возвращаемого значения будут переданы вpфункция обратного вызова.
let p2 = Promise.reject(2);
let promise = Promise.all([1, p2, 3]);
promise
  .then(value => {
  console.log(value);
})
  .catch(err => {
  console.log(err); // 2
});
console.log(promise);


Если экземпляр Promise в качестве параметра определяется сам по себеcatchметод, то, как только онrejected, не заводитсяPromise.all()изcatchметод

const p1 = new Promise(resolve => {
  resolve("hello");
})
.then(result => result)
.catch(e => e);

const p2 = new Promise(() => {
  throw new Error("报错了");
})
.then(result => result)
.catch(e => e); // p2实际上是catch返回的promise实例

Promise.all([p1, p2])
  .then(result => console.log(result)) // ['hello', Error: 报错了]
  .catch(e => console.log(e)); // 不触发

Promise.race()

Promise.race()Этот метод также заключается в переносе нескольких экземпляров Promise в новый экземпляр Promise.
Promise.race()Параметры метода те же, что иPromise.all()Метод тот же, если это не экземпляр промиса, сначала будет вызван следующийPromise.resolve()метод, который преобразует параметр в экземпляр Promise для дальнейшей обработки.

const p = Promise.race([p1, p2, p3]);

В приведенном выше коде, покаp1,p2,p3Один из экземпляров первым меняет состояние,pстатус меняется соответственно. Возвращаемое значение экземпляра Promise, который изменился первым, передается вpфункция обратного вызова.

Promise.allSettled() 

Promise.allSettled()Метод принимает массив экземпляров Promise в качестве параметров, заключенных в новый экземпляр Promise. Просто подождите, пока все эти экземпляры параметров вернут результаты, либоfulfilledещеrejected, экземпляр оболочки завершится, и параметры будут такими же, какPromise.all()так же

let p2 = Promise.reject(2);
let promise = Promise.allSettled([1, p2, 3]);
promise.then(value => {
  console.log(value); // [{status: "fulfilled", value: 1},{status: "rejected", reason: 2},{status: "fulfilled", value: 3}]
});
console.log(promise);

Promise.allSettled()Состояние возвращенного экземпляра обещания может стать толькоresolved, параметр, полученный его функцией слушателя, представляет собой массив, каждый элемент массива является объектом, каждый объект имеет атрибут состояния, значением этого атрибута может быть только строкаfulfilledили строкаrejected.fulfilled, объект имеетvalueАтрибуты,rejectedКогдаreasonАтрибут, соответствующий возвращаемому значению двух состояний.

Promise.any()

Этот метод принимает набор экземпляров Promise в качестве параметров, заключает его в новый экземпляр Promise и возвращает его. Пока один из экземпляров параметра становитсяfulfilledсостояние, экземпляр оболочки становитсяfulfilledсостояние; если все экземпляры параметров становятсяrejectedсостояние, экземпляр оболочки становитсяrejectedусловие.
Promise.any()а такжеPromise.race()Способ очень похож, только с одним отличием, то есть не станет из-за некого промисаrejectedстатус заканчивается.

let p1 = Promise.reject(1);
let p2 = Promise.reject(2);
let promise = Promise.any([p1, p2, 3]);
promise.then(value => {
  console.log(value); // 3
});
console.log(promise);

Когда все экземпляры возвращают статусrejectedчас,Promise.any()вернет состояние экземпляра, котороеrejected

let p1 = Promise.reject(1);
let p2 = Promise.reject(2);
let promise = Promise.any([p1, p2]);
promise
  .then(value => {
  console.log(value);
})
  .catch(value => {
  console.log(value); // AggregateError: All promises were rejected
});
console.log(promise);

Promise.try()

В реальной разработке часто встречается: не знаю или не хочу различать, функцииfЭто синхронная функция или асинхронная операция, но вы хотите использовать Promise для ее обработки. Потому что таким образом вы можетеfВключать ли асинхронные операции, использовать обаthenМетод определяет следующий шаг процесса, сcatchметод обработкиfОшибка выброшена. Обычно используется следующая запись.

const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now

Приведенное выше обозначение имеет недостаток, т. е. еслиfЭто функция синхронизации, тогда она будет выполняться в конце события конца этого колеса.
Учитывая, что это очень распространенное требование, в настоящее времяпредложение,поставкаPromise.tryМетод заменяет вышеупомянутое письмо.

const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next