Автор: холодная трава
WeChat: hancao97
Введение: другой программист Beidrift, добро пожаловать, чтобы добавить WeChat для критики и исправления коммуникационных технологий.
письменный фон
Собственно, то, что я хотел написать сначала, было неPromiseЭта статья, но я хотел бы обобщить некоторыеaxiosРелевантные знания, но написание статей должно основываться на них, если вы хотите хорошо говоритьaxios, то вы должны понять его пред-знание, так что я думаю, что мы могли бы также сделать это в серию, так что есть изPromise,Ajaxначать, говоритьAxiosстроить планы. На самом деле я считаю, что все, кто занимается фронтенд-разработкой, должны быть правы.PromiseЯ не чужой, так что вы, ребята, можете следовать за мной.PromiseЭти базовые знания повторяются.
Эта статья будет включать:
-
Promiseпредставлять -
PromiseФункции -
Promiseиспользовать - Согласно использованию и характеристикам предыдущего текста, рукописный
Promise
Без лишних слов, давайте начнем!
Введение в промисы
Обещание происхождения и использования
-
PromiseВпервые он был предложен и реализован в сообществе, а стандарт языка был написан на ES6.
совет: ES6 — это шестая редакция ECMA-262. Эта редакция содержит наиболее важные усовершенствования за всю историю этой спецификации. Javascript — это реализация спецификации ECMA-262.
-
PromiseЭто решение для асинхронного программирования, которое является более разумным, мощным и элегантным, чем традиционное решение с функцией обратного вызова. - Синтаксически:Используйте конструктор Promise для инкапсуляции асинхронных операций для создания экземпляров Promise.
- Функционально:Объект обещания используется для инкапсуляции асинхронной операции и предоставления унифицированного API, чтобы различные асинхронные операции могли обрабатываться одинаково.
Общие сценарии асинхронного программирования
- файловые операции fs
require('fs').readFile('./index.html',(err,data)=>{
// 回调函数
})
- Аякс операции
$.get('/api/getUser',(data)=>{
//handleData();
})
- таймер
setTimeout(() => {
console.log('timeout');
},1000);
Зачем использовать обещания
- Поддержка цепочек вызовов, выражение асинхронных операций в процессе синхронных операций, что может решить проблему callback hell
Что такое ад обратного вызова?
Функция обратного вызова называется вложенной, а результатом асинхронного выполнения внешней функции обратного вызова является условие выполнения вложенного обратного вызова.
// 回调地狱典型场景
asyncFunc1(opt,(...args1) => {
asyncFunc2(opt,(...args2) => {
asyncFunc3(opt,(...args3) => {
asyncFunc4(opt,(...args4) => {
//TODO: some opt
})
})
})
})
Недостатки callback hell:
Нелегко читать, нелегко обрабатывать исключения, не способствует физическому и умственному удовольствию.
- Более гибкий способ указания функций обратного вызова.
Традиционный способ:Необходимо указать перед запуском асинхронной задачи
Обещания:Вы можете отслеживать состояние асинхронных задач в любое время и в любое время указать одну или несколько функций обратного вызова.
- Обещания предоставляют унифицированный API, упрощающий управление асинхронными операциями.
Какие API предоставляются, будет уточнено при последующем использовании.
Особенности обещания
Свойства промисов
- Состояние объекта не зависит от внешнего мира
PromiseОбъект представляет собой асинхронную операцию и имеет три состояния:pendingв ходе выполнения,fulfilledуспех,rejectedпотерпеть неудачу
Состояние можно изменить только после завершения асинхронной операции, и никакие другие операции не могут его изменить. Состояние хранится в объектах Promise[[PromiseState]]в свойствах.
// Promise的两个属性:
let promiseA = new Promise((resolve,reject)=>{resolve();})
// 状态对应promiseA的[[PromiseState]]字段。
let promiseB = new Promise((resolve,reject)=>{resolve(1111);})
// [[PromiseResult]]的值为 1111,这个字段用于存储 resolve(val)或者reject(val)的参数[val]
Совет: обещание должно быть обещанием, и по этой причине подумайте о том, как это романтично.
- Как только состояние изменится, оно больше не изменится
Есть только две возможности для изменения состояния объекта Promise: отPending 变为 Resolvedи изPending 变为 Rejected. Пока происходят эти две ситуации, состояние заморожено и больше не изменится, и этот результат будет всегда поддерживаться. Даже если изменение уже произошло, если вы добавите callback-функцию к объекту Promise, вы немедленно получите этот результат. Это совсем не то, что событие, характеристика события в том, что если вы пропустите его и послушаете снова, вы не получите результата.
Недостатки обещаний
- Однажды вновь созданный,выполнить немедленно, вы не можете отменить на полпути
-
PromiseВнутренние ошибки, которые не могут быть отражены извне -
pendingНевозможно узнать, на какой стадии он продвинулся.
Обещания использования
Создание экземпляра обещания
const promise = new Promise((resolve, reject) => {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Конструктор Promise принимает в качестве параметра функцию, два параметра которойresolveа такжеreject. Это две функции, предоставляемые движком JavaScript, без их самостоятельного развертывания.
- разрешить функцию: изменить состояние объекта Promise с «незавершенного» на «успешное» (то есть с
pendingсталиfulfilled), вызывается при успешном выполнении асинхронной операции, и результат асинхронной операции передается в качестве параметра; - функция отклонения: изменить состояние объекта Promise с «невыполнено» на «не выполнено» (т. е. с
pendingсталиrejected), вызываемый при сбое асинхронной операции, и передает сообщение об ошибке асинхронной операции в качестве параметра.
Promise.prototype.then
После создания экземпляра Promise вы можете использоватьthenспособы оговариваются отдельноfulfilledстатус иrejectedфункция обратного вызова состояния
в конечном итоге вернет новыйPromiseобъект
подсказка: функция обратного вызова в then не является обязательной
promise.then(function(value) {
// fulfilled状态的处理
}, function(error) {
// rejected状态的处理
});
Promise.prototype.catch
catch используется для обработки состояния какrejectedфункция обратного вызова
в конечном итоге вернет новыйPromiseобъект
promise.catch((err)=>{
handleReject();
//
})
Promise.resolve
Возвращает успешный или неудачный объект Promise
let promiseA = Promise.resolve(1);
// 如果传入的参数为非Promise类型的对象,则返回的结果为成功的Promise对象
let PromiseB = Promise.resolve(new Promise((resolve,reject)=>{
reject('err');
})
// 如果传入的参数为Promise对象,则参数Promise返回的结果就是 Promise.resolve返回的结果
// 比如这时return 一个[[PromiseResult]]的值为err的Promise对象
Promise.reject
Возвращает неудачный объект Promise
let PromiseA = Promise.reject(new Promise((resolve,reject)=>{
resolve('err');
})
// 无论传入是啥,就返回一个失败的Promise对象,[[PromiseResult]]的值为 Promise.reject的参数
Promise.all
Полученный аргумент представляет собой массив из n объектов Promise.
подсказки: возвращаемый результат — новое обещание. Оно будет успешным, только если все объекты Promise будут успешными.
let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.all([promise1,promise2,promise3]);
//此时输出为 [[PromiseState]]是rejected,[[PromiseResult]]是3的Promise
const res = Promise.all([promise1,promise2]);
//此时输出为 [[PromiseState]]是fulfilled,[[PromiseResult]]是[1,2]的Promise
Promise.race
Полученный аргумент представляет собой массив из n объектов Promise.
подсказки: возвращаемый результат — это новый промис, а статус результата первого выполненного промиса — это статус конечного результата.
let promise1 = Promise.resolve(1);
let promise2 = Promise.resolve(2);
let promise3 = Promise.reject(3);
const res = Promise.race([promise1,promise2,promise3]);
//此时输出为 [[PromiseState]]是fulfilled,[[PromiseResult]]是1的Promise
Общая проблема
Как изменить состояние объекта Promise
- resolve() // pending => fulfilled
- reject() // pending => rejected
- выбросить ошибку выбросить 'err' // в ожидании => отклонено
Возвращаемый результат promise.then()
- Если выброшено исключение, верните отклоненный объект Promise
- Если возвращается какое-либо значение не-обещающего типа, возвращается объект обещания с разрешенным состоянием.
- Если возвращается новое обещание, результат этого обещания становится результатом нового обещания.
Почему обещания могут быть связаны?
Потому что возвращаемое значение всех API обещаний, таких как then, catch, all, race и т. д., является новым объектом обещания.
Таким образом, вы можете продолжать вызывать метод обещания и связывать задачи таким образом.
Исключение проникновения промисов
- При цепочке с обещанием в конце может быть указан неудачный обратный вызов.
- Любые предыдущие ошибки будут переданы неудачному обратному вызову в конце для обработки.
let p1 = Promise.resolve(1);
p1.then((value)=>{
console.log(11);
}).then((value)=>{
throw 'err';
}).then((value)=>{
console.log(22);
}).catch(err=>{
console.log(err);
})
//输出: 11 err
Разорвите цепочку обещаний
let p1 = Promise.resolve(1);
p1.then((value)=>{
console.log(11);
}).then((value)=>{
console.log(22);
}).then((value)=>{
console.log(33);
}).catch(err=>{
console.log(err);
})
//输出:11 22 33
Так как же нам прервать совместную отладку этой функции обратного вызова?
let p1 = Promise.resolve(1);
p1.then((value)=>{
console.log(11);
}).then((value)=>{
console.log(22);
return new Promise(()=>{});
}).then((value)=>{
console.log(33);
}).catch(err=>{
console.log(err);
})
//输出:11 22
Ответ состоит в том, чтобы вернуть статусpendingизpromiseобъект
Рукописное обещание
Код повторяется шаг за шагом, вы можете проверить это, я написал комментарии более понятно~
Реализация конструктора
Наш первый шаг — закончитьPromiseКонструктор, мы просто думаем о конструкторе, по сути, это получение функции-исполнителя, функция-исполнитель имеет два параметра, этот метод широк для измененияPromiseСостояние и результат объекта. хорошо, просто сделай это!
tips:
- Уведомление! Throw err также может изменить состояние и результат Promise!
- Состояние обещания можно изменить только один раз, что необходимо ограничить
// 万里长城第一步
function Promise(executor){
this.promiseState = 'pending';
this.promiseResult = null;
const resolve = val => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
this.promiseState = 'fulfilled';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = val;
}
const reject = err => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
this.promiseState = 'rejected';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = err;
}
// 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
try{
/*
* 同步执行执行器函数
* 执行器函数接收两个参数,一个是resolve,一个是reject
*/
executor(resolve,reject);
} catch(err) {
reject(err);
}
}
Реализация метода then
Во-первых, согласно предыдущему, затем поддерживает два параметра, которые являются успешными и неудачными обратными вызовами, и эти два параметра могут передаваться.
Во-вторых, из-за проблемы асинхронных задач и поддержки множественных обратных вызовов нам необходимо хранить функцию обратного вызова в массиве, поэтому вводится новая переменная,callbackList
И мы должны отметить, что возвращаемый результат then такжеPromiseТема [Если не помните, оглянитесь назад~]
// 万里长城今犹在~
function Promise(executor){
//保存promise状态
this.promiseState = 'pending';
//保存promise结果
this.promiseResult = null;
//用于保存异步回调函数列表
this.callbackList = [];
const resolve = val => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
this.promiseState = 'fulfilled';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = val;
// 调用成功的回调【callbackList存起来的】
for(let callback of this.callbackList){
callback.onResolved(val);
}
}
const reject = err => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
this.promiseState = 'rejected';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = err;
// 调用失败的回调【callbackList存起来的】
for(let callback of this.callbackList){
callback.onRejected(err);
}
}
// 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
try{
/*
* 同步执行执行器函数
* 执行器函数接收两个参数,一个是resolve,一个是reject
*/
executor(resolve,reject);
} catch(err) {
reject(err);
}
}
//then方法
Promise.prototype.then = function(onResolved,onRejected){
const self = this;
// then方法会返回Promise
return new Promise((resolve,reject) => {
// 对返回值的处理进行封装
const handleCallback = (callback) => {
// 如果回调函数中抛出错误,则reject
try{
// 需要依据回调的返回结果确定then方法的返回值
// 现在的this会指向return的promise对象,所以使用self
const res = callback(self.promiseResult);
if(res instanceof Promise){
//如果回调返回结果是个Promise
res.then(val => {
resolve(val);
},err => {
reject(err);
})
}else{
// 返回结果不是Promise
resolve(res);
}
}catch(err){
reject(err);
}
}
//调用回调函数
if(this.promiseState === 'fulfilled'){
handleCallback(onResolved);
}
if(this.promiseState === 'rejected'){
handleCallback(onRejected);
}
/*
* 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
* 所以要保存回调函数
* 因为promise实例阔以指定多个回调,于是采用数组
*/
if(this.promiseState === 'pending'){
this.callbackList.push({
onResolved:() => {
handleCallback(onResolved);
},
onRejected:() => {
handleCallback(onRejected);
}
})
}
})
}
Реализация метода catch
Мы используем метод then для реализации метода catch, но метод catch может обрабатывать проникновение исключений [я говорил ранее~]
// 我想找人陪我去长城玩,哈哈哈哈
function Promise(executor){
//保存promise状态
this.promiseState = 'pending';
//保存promise结果
this.promiseResult = null;
//用于保存异步回调函数列表
this.callbackList = [];
const resolve = val => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
this.promiseState = 'fulfilled';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = val;
// 调用成功的回调【callbackList存起来的】
for(let callback of this.callbackList){
callback.onResolved(val);
}
}
const reject = err => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
this.promiseState = 'rejected';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = err;
// 调用失败的回调【callbackList存起来的】
for(let callback of this.callbackList){
callback.onRejected(err);
}
}
// 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
try{
/*
* 同步执行执行器函数
* 执行器函数接收两个参数,一个是resolve,一个是reject
*/
executor(resolve,reject);
} catch(err) {
reject(err);
}
}
//then方法
Promise.prototype.then = function(onResolved,onRejected){
const self = this;
//处理异常穿透 并且为onResolved,onRejected设置默认值。因为这两个参数可以都不传
if(typeof onRejected !== 'function'){
onRejected = err => {
throw err;
}
}
if(typeof onResolved !== 'function'){
onResolved = val => val;
}
// then方法会返回Promise
return new Promise((resolve,reject) => {
// 对返回值的处理进行封装
const handleCallback = (callback) => {
// 如果回调函数中抛出错误,则reject
try{
// 需要依据回调的返回结果确定then方法的返回值
// 现在的this会指向return的promise对象,所以使用self
const res = callback(self.promiseResult);
if(res instanceof Promise){
//如果回调返回结果是个Promise
res.then(val => {
resolve(val);
},err => {
reject(err);
})
}else{
// 返回结果不是Promise
resolve(res);
}
}catch(err){
reject(err);
}
}
//调用回调函数
if(this.promiseState === 'fulfilled'){
handleCallback(onResolved);
}
if(this.promiseState === 'rejected'){
handleCallback(onRejected);
}
/*
* 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
* 所以要保存回调函数
* 因为promise实例阔以指定多个回调,于是采用数组
*/
if(this.promiseState === 'pending'){
this.callbackList.push({
onResolved:() => {
handleCallback(onResolved);
},
onRejected:() => {
handleCallback(onRejected);
}
})
}
})
}
//catch方法
Promise.prototype.catch = function(onRejected) {
// 我们可以直接使用then方法实现
return this.then(undefined,onRejected);
}
Реализация метода Promise.resolve
Просто, ничего не говори
//resolve方法
Promise.resolve = function(val) {
//返回值的情况在前文说过,可以在 Promise的使用一章找到
return new Promise((resolve,reject)=>{
if(val instanceof Promise){
val.then(val => {
resolve(val);
}, err => {
reject(err);
});
}else{
resolve(value);
}
})
}
Реализация метода Promise.reject
Так проще, ничего не говори
//reject方法
Promise.reject = function(err) {
//返回值的情况在前文说过,可以在 Promise的使用一章找到
return new Promise((resolve,reject)=>{
reject(err);
})
}
Реализация метода Promise.all
Это относительно просто. Оглядываясь назад на предыдущее использование и возвращаемое значение Promise.all, легко понять ~
//可以先去回顾一下all方法的用法
//all
Promise.all = function(promiseList) {
let count = 0;
let res = [];
const length = promiseList.length;
return new Promise((resolve,reject)=>{
for(let i = 0;i < length; i++){
promiseList[i].then(val => {
count++;
res[i] = val;
if(count === length){
resolve(res);
}
},err => {
reject(err);
});
}
})
}
Реализация метода Promise.race
Это относительно просто. Оглядываясь назад на предыдущее использование и возвращаемое значение Promise.race, легко понять ~
//race
//要结束了!
Promise.race = function(promiseList) {
const length = promiseList.length;
//谁先完成谁就决定结果!
return new Promise((resolve,reject)=>{
for(let i = 0;i < length; i++){
promiseList[i].then(val => {
resolve(val);
},err => {
reject(err);
});
}
})
}
Полный код и детальная обработка
подсказки: функция обратного вызова деталей является асинхронной, и мы используем setTimeout для ее переноса
function Promise(executor){
//保存promise状态
this.promiseState = 'pending';
//保存promise结果
this.promiseResult = null;
//用于保存异步回调函数列表
this.callbackList = [];
const resolve = val => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为成功fulfilled
this.promiseState = 'fulfilled';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = val;
setTimeout(() => {
// 调用成功的回调【callbackList存起来的】
for(let callback of this.callbackList){
callback.onResolved(val);
}
})
}
const reject = err => {
// 状态只能修改一次
if(this.promiseState !== 'pending') return;
// 1. 要去修改Promise对象的状态([[promiseState]]),置为失败rejected
this.promiseState = 'rejected';
// 2. 要去修改Promise对象的状态([[promiseResult]])
this.promiseResult = err;
setTimeout(() => {
// 调用失败的回调【callbackList存起来的】
for(let callback of this.callbackList){
callback.onRejected(err);
}
})
}
// 为什么要加try catch 是因为,throw err也相当于调用reject了【前面说过没看过的去补课】
try{
/*
* 同步执行执行器函数
* 执行器函数接收两个参数,一个是resolve,一个是reject
*/
executor(resolve,reject);
} catch(err) {
reject(err);
}
}
//then方法
Promise.prototype.then = function(onResolved,onRejected){
const self = this;
//处理异常穿透 并且为onResolved,onRejected设置默认值。因为这两个参数可以都不传
if(typeof onRejected !== 'function'){
onRejected = err => {
throw err;
}
}
if(typeof onResolved !== 'function'){
onResolved = val => val;
}
// then方法会返回Promise
return new Promise((resolve,reject) => {
// 对返回值的处理进行封装
const handleCallback = (callback) => {
// 如果回调函数中抛出错误,则reject
try{
// 需要依据回调的返回结果确定then方法的返回值
// 现在的this会指向return的promise对象,所以使用self
const res = callback(self.promiseResult);
if(res instanceof Promise){
//如果回调返回结果是个Promise
res.then(val => {
resolve(val);
},err => {
reject(err);
})
}else{
// 返回结果不是Promise
resolve(res);
}
}catch(err){
reject(err);
}
}
//调用回调函数
if(this.promiseState === 'fulfilled'){
setTimeout(()=>{
handleCallback(onResolved);
})
}
if(this.promiseState === 'rejected'){
setTimeout(()=>{
handleCallback(onRejected);
})
}
/*
* 如果是pending状态,则异步任务,在改变状态的时候去调用回调函数
* 所以要保存回调函数
* 因为promise实例阔以指定多个回调,于是采用数组
*/
if(this.promiseState === 'pending'){
this.callbackList.push({
onResolved:() => {
handleCallback(onResolved);
},
onRejected:() => {
handleCallback(onRejected);
}
})
}
})
}
//catch方法
Promise.prototype.catch = function(onRejected) {
// 我们可以直接使用then方法实现
return this.then(undefined,onRejected);
}
//resolve方法
Promise.resolve = function(val) {
//返回值的情况在前文说过,可以在 Promise的使用一章找到
return new Promise((resolve,reject)=>{
if(val instanceof Promise){
val.then(val => {
resolve(val);
}, err => {
reject(err);
});
}else{
resolve(value);
}
})
}
//reject方法
Promise.reject = function(err) {
//返回值的情况在前文说过,可以在 Promise的使用一章找到
return new Promise((resolve,reject)=>{
reject(err);
})
}
//all
Promise.all = function(promiseList) {
let count = 0;
let res = [];
const length = promiseList.length;
return new Promise((resolve,reject)=>{
for(let i = 0;i < length; i++){
promiseList[i].then(val => {
count++;
res[i] = val;
if(count === length){
resolve(res);
}
},err => {
reject(err);
});
}
})
}
//race
Promise.race = function(promiseList) {
const length = promiseList.length;
//谁先完成谁就决定结果!
return new Promise((resolve,reject)=>{
for(let i = 0;i < length; i++){
promiseList[i].then(val => {
resolve(val);
},err => {
reject(err);
});
}
})
}
На этом глава нашего рукописного Обещания окончена~
заключительные замечания
Советы: На самом деле, я очень хочу пожаловаться на себя, почему я всегда говорю куриная кровь или куриный суп в конце блога, ха-ха
PromiseСлово должно быть обещанием. Я использую эту статью как свое новое начало, начинаю с основы и отказываюсь быть слишком амбициозным.
Я обещаю, что мои убеждения и стремления всегда будут такими же
Я обещаю позволить себе увидеть красоту мира
Я обещаю, что всегда буду стремиться быть хорошим инженером
я обещаюКаким бы ни был результат, те, кому он нравится, будут упорно и мужественно добиваться его без сожалений.
...
Наконец, пусть я буду старым и кровавым навсегда, пусть мир будет навеки в мире
Друзья, лук~