«Серия ES6» полностью разбирается в Promise (с упражнениями)

ECMAScript 6
«Серия ES6» полностью разбирается в Promise (с упражнениями)

Чем больше вы знаете, тем больше вы не знаете
点赞Посмотри еще раз, аромат остался в руке, и слава

Причины обещаний

Когда-то нам приходилось использовать множество callback-функций, чтобы получить результаты асинхронных вызовов.Давайте рассмотрим следующий пример:

Получить данные сервера через ajax Jquery

let url1 = 'http://xxx.xxx.1';
$.ajax({
    url:url1,
    error:function (error) {},
    success:function (data1) {
        console.log(data1);
    }
});

Когда необходимо отправлять несколько асинхронных запросов, и каждый запрос нужно взаимозависимость, мы можем решить в гнезненном порядке

let url1 = 'http://xxx.xxx.1';
let url2 = 'http://xxx.xxx.2';
let url3 = 'http://xxx.xxx.3';
$.ajax({
    url:url1,
    error:function (error) {},
    success:function (data1) {
        console.log(data1);
        $.ajax({
            url:url2,
            data:data1,
            error:function (error) {},
            success:function (data2) {
                console.log(data2);
                $.ajax({
                    url:url3,
                    data,
                    error:function (error) {},
                    success:function (data3) {
                        console.log(data3);
                    }
                });
            }
        });
    }
});

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

Основные проблемы с этим режимом кодирования:

  • Код раздут.
  • Плохая читабельность.
  • Муфта слишком высокая, а ремонтопригодность плохая.
  • Плохая возможность повторного использования кода.
  • Разводить жуков легко.
  • Исключения могут обрабатываться только в обратных вызовах.

Появление Promise заключается в решении различных проблем, вызванных функциями обратного вызова.

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

Promise — это решение для асинхронного программирования, более разумное и мощное, чем традиционные асинхронные решения [функция обратного вызова] и [событие].

  • Синтаксически обещание — это объект, из которого можно получить сообщение для асинхронной операции.
  • Это обещание в его первоначальном значении, обещание, что со временем оно даст вам результат.

сравнение написания кода

Сначала инкапсулируйте ajax-метод, поддерживающий Promise. Неважно, если вы не понимаете этот код. Механизм выполнения Promise будет объяснен шаг за шагом позже.

function request(url,data = {}){
    return new Promise((resolve,reject)=>{
        $.ajax({
            url,
            data,
            success:function (data) {
                resolve(data);
            },
            error:function (error) {
                reject(error);
            }
        })
    });
}

Используйте метод запроса для реализации предыдущих нескольких взаимозависимых сетевых запросов.

let url1 = 'http://xxx.xxx.1';
let url2 = 'http://xxx.xxx.2';
let url3 = 'http://xxx.xxx.3';
request(url1)
    .then((data)=>{
        console.log(data);
        return request(url2,data)
    })
    .then((data)=>{
        console.log(data);
        return request(url3,data)
    })
    .then((data)=>{
        console.log(data)
    })
    .catch((error)=>{
        console.log(error);
    });

Свойства промисов

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

  • в ожидании (состояние ожидания)
  • выполненный
  • отклоненный

Окончательная стоимость и причина отказа

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

Связь между состоянием и состоянием, связь между состоянием и конечным значением и причина отклонения

  • ожидающие могут быть перенесены в выполненные или отклоненные
  • выполнено не может быть переведено в другие состояния и должно иметь неизменное конечное значение
  • Отклонено не может мигрировать в другие состояния, должна иметь неременную базу

Использование промисов

Конструктор

Promise — это конструктор, который возвращает объект обещания с помощью оператора new.

Конструктор принимает функцию-исполнитель в качестве параметра

Функция-исполнитель имеет два параметра типа функции: разрешить и отклонить.

let p = new Promise((resolve,reject)=>{
     // 在 excutor 函数中执行异步操作
     // 当异步操作完成后,调用 resolve 函数或 reject 函数
});
  • При вызове конструктора функция excutor выполняется немедленно как синхронный код.
  • Обычно мы выполняем наши асинхронные операции в функции-исполнителе.
  • Когда функции разрешения и отклонения не вызываются, объект обещания находится в состоянии ожидания.
let p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('p1');
    },1000);
});
// p1 的状态一直为 pending
  • При вызове функции разрешения параметры разрешения являются необещаемыми объектами, объектами, которые нельзя использовать
    • Параметр функции разрешения, как конечное значение объекта обещания
    • Состояние объекта обещания становится выполненным
let p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('p2');
        resolve('我是p2的终值')
    },1000);
});
// 代码执行,1000ms内,p2 的状态为 pending
// 代码执行,1000ms后,p2 的状态为 fulfilled
// 代码执行,1000ms后,p2 的终值为 '我是p2的终值'
  • При вызове функции разрешения параметр разрешения является объектом обещания.
    • Состояние, окончательное значение и отклонение объекта обещания синхронизированы с объектом входящего обещания
let p = new Promise((resolve,reject)=>{
    reject('error')
})
let p1 = new Promise((resolve,reject)=>{
    resolve(p)
})
// p1 的状态为 rejected ,拒因为 error
  • При вызове функции разрешения параметр разрешения является доступным объектом.
    • Объект thenable будет расширен.Состояние, конечное значение и отклонение объекта обещания зависят от результата вызова метода then объекта thenable.
let thenable1 = {
    then:function(resolve,reject){
        resolve(1)
    }
}
let thenable2 = {
    then:function(resolve,reject){
        reject(2)
    }
}
let thenable3 = {
    then:function(resolve,reject){
        throw new Error(3)
    }
}
let thenable4 = {
    then:function(fn1,fn2){
        //不调用 fn1 fn2
    }
}
let p1 = new Promise((resolve,reject)=>{
    resolve(thenable1);
})
let p2 = new Promise((resolve,reject)=>{
    resolve(thenable2);
})
let p3 = new Promise((resolve,reject)=>{
    resolve(thenable3);
})
let p4 = new Promise((resolve,reject)=>{
    resolve(thenable4);
})
// p1 的状态为 fulfilled 终值为 1
// p2 的状态为 rejected  终值为 2
// p3 的状态为 rejected  拒因为 Error:3
// p4 的状态为 pending
  • При вызове функции reject параметр функции reject используется как отклонение объекта promise
  • Состояние объекта обещания становится отклоненным
let p3 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('p3');
        reject('我是p3的拒因')
    },1000);
});
// 代码执行,1000ms内,p3 的状态为 pending
// 代码执行,1000ms后,p3 的状态为 rejected
// 代码执行,1000ms后,p3 的拒因为 '我是p3的拒因'

методы для объектов-обещаний

затем метод:

Promise предоставляет метод THEN для доступа к его окончательным значениям и отказам.

Метод then обещания принимает два параметра:

promise.then(onFulfilled, onRejected);
  • Функция onFulfilled используется для получения окончательного значения, когда состояние обещания становится выполненным.
  • Функция onRejected используется для получения отказа, когда статус обещания становится отклоненным.
new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('异步任务获取的数据')
    },50)
}).then((data)=>{
    console.log(data)
})
// 异步任务获取的数据
new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject(new Error('异步任务异常'))
    },50)
}).then(null,(error)=>{
    console.log(error)
})
// Error: 异步任务异常
new Promise((resolve,reject)=>{
    throw new Error('抛出一个异常');
}).then(null,(error)=>{
    console.log(error)
})
// Error: 抛出一个异常
Параметры onFulfilled и onRejected являются необязательными.
  • Если onFulfilled не является функцией, ее нужно игнорировать.
  • Если onRejected не является функцией, ее нужно игнорировать.
навернутая особенность

Если onFulfilled является функцией:

  • Его нужно вызывать, когда обещание выполнено, а его первый параметр — конечное значение обещания.
  • Его нельзя вызвать, пока обещание не будет выполнено
  • Его нельзя вызывать более одного раза
атрибут onRejected

Если onRejected является функцией:

  • Когда обещание должно отказаться выполняться, оно должно быть вызвано, его первый параметр для обещания данных
  • Его нельзя вызвать, пока обещание не будет отклонено
  • Его нельзя вызывать более одного раза
Когда вызывать onFulfilled и onRejected
  • Вызывается, когда состояние объекта обещания становится выполненным или отклоненным
  • onFulfilled, onRejected всегда являются асинхронными вызовами
  • onFulfilled, onRejected обрабатываются как микрозадачи в очереди событий
console.log(1);
setTimeout(function(){
    console.log(2)
},0)
new Promise((resolve,reject)=>{
    resolve(3);
}).then((data)=>{
    console.log(data);
})
console.log(4)
// print: 1 4 3 2
Требования к вызову для onFulfilled и onRejected
  • onFulfilled и onRejected должны вызываться как функции
  • В нестрогом режиме это глобальный объект
  • В строгом режиме это не определено
function fn1(){
    new Promise((resolve)=>{
        resolve();
    }).then(function(){
        console.log(this)
    })
}
function fn2(){
    "use strict";
    new Promise((resolve)=>{
        resolve();
    }).then(function(){
        console.log(this)
    })
}
fn1(); // print: window
fn2(); // print: undefined
Множественные вызовы метода then
  • Метод then может вызываться несколько раз одним и тем же объектом обещания.
  • Метод then возвращает новый объект обещания.
  • Когда обещание успешно выполнено, все onFulfilled должны быть вызваны в том порядке, в котором они были зарегистрированы.
  • Когда обещание отклонено, все onRejected должны быть вызваны в порядке их регистрации.
let p = new Promise((resolve)=>{
    resolve()
});
let p1 = p.then(()=>{
    console.log('异步执行,第一个onFulfilled');
});
let p2 = p.then(()=>{
    console.log('异步执行,第二个onFulfilled');
});
console.log(p1.constructor === Promise);
console.log(p === p1);
console.log(p === p2);
console.log(p1 === p2);
// print: true
// print: false
// print: false
// print: false
// print: 异步执行,第一个onFulfilled
// print: 异步执行,第二个onFulfilled
возвращаемое значение метода then

Метод then возвращает объект обещания

promise2 = promise1.then(onFulfilled, onRejected);   
  • Если onFulfilled и onRejected возвращают значение x, которое не является объектом обещания или объектом, который можно использовать, состояние обещания2 выполняется, и окончательное значение равно x.
let p = new Promise((resolve,reject)=>{
    throw new Error();
});
let p1 = p.then(null,(data)=>{
    return '我是p2的终值'
});
p1.then((data)=>{
    console.log(data)
});
// print: 我是p2的终值
  • Если onFulfilled и onRejected возвращают значение x объекта обещания, состояние, окончательное значение и отклонение promise2 синхронизируются с x
let p1 = new Promise((resolve,reject)=>{
    resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
    reject(2)
})
let p3 = new Promise((resolve)=>{
    resolve()
})
let p4 = p3.then(()=>{
    return p1;
})
let p5 = p3.then(()=>{
    return p2;
})
// p4 的状态为 fulfilled 终值为 1
// p5 的状态为 rejected  拒因为 2
  • Если onFulfilled и onRejected возвращают объект thenable, то объект thenable будет расширен.Состояние, конечное значение и отклонение promise2 зависят от результата вызова метода then объекта thenable.
let thenable1 = {
    then:function(resolve,reject){
        resolve(1)
    }
}
let thenable2 = {
    then:function(resolve,reject){
        reject(2)
    }
}
let p1 = new Promise((resolve,reject)=>{
    resolve()
})
let p2 = p1.then(()=>{
    return thenable1;
})
let p3 = p1.then(()=>{
    return thenable2;
})
// p2 的状态为 fulfilled 终值为 1
// p3 的状态为 rejected  拒因为 2
  • Если onFulfilled или onRejected выдает исключение e , состояние promise2 отклонено, а отклонение равно e
let p = new Promise((resolve,reject)=>{
    resolve();
});
let p1 = p.then((data)=>{
    throw new Error('error')
});
p1.then(null,(err)=>{
    console.log(err);
});
// print: Error: error
  • Если onFulfilled не является функцией и обещание1 успешно выполнено, состояние обещания2 выполнено, и окончательное значение является окончательным значением обещания1.
let p = new Promise((resolve,reject)=>{
    resolve('我是p1的终值');
});
let p1 = p.then(null,null);
p1.then((data)=>{
    console.log(data);
});
// print: 我是p1的终值
  • Если onRejected не является функцией, а обещание1 отказывается выполняться, статус обещания2 отклоняется, поскольку обещание1 отклонено.
let p = new Promise((resolve,reject)=>{
    reject('我是p1的拒因');
});
let p1 = p.then(null,null);
p1.then(null,(err)=>{
    console.log(err);
});
// print:我是p1的拒因
  • Если во время выполнения onFulfilled и onRejected возникает исключение, статус promise2 отклоняется.
let p = new Promise((resolve,reject)=>{
    resolve('我是p的终值');
});
let p1 = p.then((data)=>{
    throw new Error('异常')
});
p1.then(null,(err)=>{
    console.log(err);
});
// print:Error: 异常
Свойства проникновения терминальной стоимости и отклонения
  • Если состояние обещания становится выполненным, то метод не регистрируется на выполненном
    • Состояние объекта обещания, возвращаемое методом then, становится выполненным.
    • Окончательное значение объекта обещания, возвращаемое методом then, совпадает с окончательным значением исходного объекта обещания.
  • Если состояние обещания становится отклоненным, то метод не регистрируется onRejected
    • Состояние объекта обещания, возвращаемое методом then, становится отклоненным.
    • Причина отклонения объекта обещания, возвращаемого методом then, такая же, как и отклонение исходного объекта обещания.
let p1 = new Promise((resolve,reject)=>{
    resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
    reject(2)
})

let p3 = p1.then(null,null);
let p4 = p2.then(null,null);
// p3 的状态是 fulfilled 终值 1
// p4 的状态是 rejected  拒因 2


p5 = p3.then(null,null);
p6 = p4.then(null,null);
// p3 的状态是 fulfilled 终值 1
// p4 的状态是 rejected  拒因 2
  • Функция проникновения в основном используется для обработки исключений.
let fn1 = function(){}
let fn2 = function(){}
let fn3 = function(){}
let fn4 = function(){}
let fn5 = function(){}
let onError = function(){};
new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject()
    })
})
.then(fn1)
.then(fn2)
.then(fn3)
.then(fn4)
.then(fn5)
.then(null,onError)

Ошибки могут возникать в fn1, fn2, fn3, fn4 и fn5, а ненормальные ошибки могут возникать из-за функции onRejected, зарегистрированной в последней функции then.

метод ловли:

Метод Catch (FN) на самом деле является псевдонимом метода THEN (NULL, FN), возвращаемое значение метода catch и нештатная ситуация в методе catch такие же, как при вызове метода THEN.

new Promise((resolve,reject)=>{
    reject()
}).then(null,function(error){

})
// 等同于
new Promise((resolve,reject)=>{
    reject()
}).catch(function(error){

})

Статические методы промисов

Promise.resolve

  • Метод Promise.resolve используется для преобразования существующих данных в объект обещания.
    • Если входной параметр является объектом обещания
      • Состояние, конечное значение и отклонение возвращаемого объекта обещания синхронизируются с входными параметрами метода Promise.resolve.
    • Если входной параметр является доступным объектом
      • Объект THENBLE расширяется, состояние объекта promise, конечное значение, отказ в зависимости от метода THEN объекта THENBLE
    • Если ввод не обещанный объект, не подлежащий дальнейшему использованию
      • Состояние возвращаемого объекта обещания выполнено
      • Окончательное значение возвращаемого объекта обещания является входным параметром метода Promise.resolve.
let p = Promise.resolve(x)
// 等价于
let p = new Promise((resolve)=>{
    resolve(x)
})

Promise.reject

  • Метод Promise.reject используется для возврата объекта обещания, чей статус отклонен, который отклонен, поскольку метод является входным.
let p = Promise.reject(x)
// 等价于
let p = new Promise((resolve,reject)=>{
    reject(x)
})

Promise.all

  • Метод Promise.all используется для переноса нескольких объектов-обещаний в новый объект-обещание.
const p = Promise.all([p1, p2, p3]);
  • P1, P2, P3 обещают объекты, если нет, вызовите обещание. Методы преобразовать в объекты обещания
  • Состояние p определяется p1, p2, p3
    • Когда состояния p1, p2, p3 все выполняются
      • статус p выполнен
      • В этот момент конечные значения p1, p2 и p3 образуют массив, который используется как конечное значение p.
    • Когда одно из состояний p1, p2 и p3 становится отклоненным
      • Статус p меняется на отклоненный
      • В это время первое состояние обещания объектов отвергнуто из-за отталкивания отталкивания из-за р
let p1 = Promise.resolve(1);
let p2 = Promise.resolve(2);
let p3 = 3;

Promise.all([p1,p2,p3]).then((data)=>{
    console.log(data); // print: [1,2,3]
})
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p2 error')
    },1000)
})
let p3 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p3 error')
    },500)
})
Promise.all([p1,p2,p3]).catch((error)=>{
    console.log(error); // print: p3 error
})

Promise.race

  • Метод Promise.race также используется для переноса нескольких объектов промисов в новый объект промисов.
const p = Promise.race([p1, p2, p3]);
  • p1, p2 и p3 являются объектами обещаний. Если нет, вызовите метод Promise.resolve, чтобы преобразовать их в объекты обещаний.
  • Состояние p определяется объектом обещания, состояние которого становится выполненным или отклоненным первым в p1, p2 и p3.
  • Окончательное значение или отклонение p определяется объектом обещания, который первым изменил состояние.
let p1 = Promise.resolve(1);
let p2 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p2 error')
    },1000)
})
let p3 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p3 error')
    },500)
})
Promise.race([p1,p2,p3]).then(data=>{
    console.log(data);
}).catch(error=>{
    console.log(error);
})
// print: 1
let p1 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        resolve(1)
    },1000)
})
let p2 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p2 error')
    },800)
})
let p3 = new Promise((resolve,reject)=>{
    setTimeout(function(){
        reject('p3 error')
    },500)
})

Promise.race([p1,p2,p3]).then(data=>{
    console.log(data);
}).catch(error=>{
    console.log(error);
})
// print: p3 error

Перехват ошибок для промисов

Когда состояние обещания отклонено и для объекта обещания используется метод catch, информация об исключении в это время будет съедена объектом обещания. Вы можете специально прослушивать необработанные ошибки отклонения, прослушивая событие unhandledRejection.

// node 环境下
process.on('unhandledRejection', error => {
    console.log('unhandledRejection', error);
});
// 浏览器下
window.addEventListener('unhandledrejection',(e)=>{
    e.preventDefault();
    console.log(e);
});

Проблемы с обещаниями

  • Промисы нельзя отменить, а промисы нельзя остановить без изменения состояния
  • Если метод then или catch не задан, конструктор (функция excutor) неверен и не может быть перехвачен.
  • В незавершенном состоянии невозможно узнать, началось ли оно только что или вот-вот завершится.

Тема обещания

Тема 1

const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})
promise.then(() => {
  console.log(3)
})
console.log(4)

Результат: 1 2 4 3

тема вторая

const promise = new Promise((resolve, reject) => {
  resolve('success1')
  reject('error')
  resolve('success2')
})

promise
  .then((data) => {
    console.log(data)
  })
  .catch((err) => {
    console.log(err)
  })

Результат: успех1

тема три

Promise.resolve(1)
  .then((data) => {
    console.log(data)
    return 2
  })
  .catch((err) => {
    return 3
  })
  .then((data) => {
    console.log(data)
  })

Результат: 1 2

тема четвертая

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

Результат: 1

пятая тема

new Promise((resolve,reject)=>{
    console.log(3);
    let p = new Promise((resolve, reject)=>{
        console.log(7);
        setTimeout(()=>{
           console.log(5);
           resolve(6); 
        },0)
        resolve(1);
    });
    resolve(2);
    p.then((arg)=>{
        console.log(arg);
    });

}).then((arg)=>{
    console.log(arg);
});
console.log(4);

Результат: 3 7 4 1 2 5

Если у вас есть какие-либо вопросы по вышеуказанным темам, вы можете общаться со мной в области сообщений.

Ссылаться на

Рекомендуемая серия статей

напиши в конце

  • Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, добро пожаловать点赞а также关注
  • Эта статья была впервые опубликована одновременно сgithub, доступны наgithubНайдите больше отличных статей вWatch & Star ★
  • Для последующих статей см.:строить планы

Добро пожаловать в публичный аккаунт WeChat【前端小黑屋】, 1–3 высококачественные высококачественные статьи публикуются каждую неделю, чтобы помочь вам в продвижении вперед.