Обещаний нет? ? Смотри сюда! ! ! Самый доступный Promise! ! !

внешний интерфейс JavaScript Promise


1. Что такое обещание? Для решения какой проблемы мы используем промисы?

Промисы — это решение для асинхронного программирования: Синтаксически обещание — это объект, от которого вы можете получить сообщение для асинхронной операции; в своем первоначальном смысле это обещание, которое обещает дать вам результат через некоторое время. Промис имеет три состояния:ожидание (состояние ожидания), выполнено (состояние успеха), отклонено (состояние отказа); как только состояние изменится, оно не изменится снова. После создания экземпляра промиса он выполняется немедленно.

Я считаю, что все часто пишут такой код:

// 当参数a大于10且参数fn2是一个方法时 执行fn2
function fn1(a, fn2) {
    if (a > 10 && typeof fn2 == 'function') {
        fn2()
    }
}
fn1(11, function() {
    console.log('this is a callback')
})

Вообще говоря, вложений обратных вызовов, с которыми мы столкнемся, не так много, обычно один или два уровня, однако в некоторых случаях, когда вложений обратных вызовов много, код будет очень громоздким, что многое внесет в наше программирование. , эта ситуация широко известна как «ад обратного вызова».

В это время наше обетование возникло и стало реальностью.

Промисы используются для решения двух проблем:

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


Во-вторых, использование обещания es6

Promise — это конструктор, у него есть знакомые методы, такие как all, reject и resolve on the own, а также знакомые методы, такие как then и catch на основе его прототипа.

потом новый

let p = new Promise((resolve, reject) => {
    //做一些异步操作
    setTimeout(() => {
        console.log('执行完成');
        resolve('我是成功!!');
    }, 2000);
});

Конструктор Promise принимает один параметр: функцию, и эта функция должна передавать два параметра:
  • resolve : функция обратного вызова после успешного выполнения асинхронной операции.
  • reject: функция обратного вызова после сбоя выполнения асинхронной операции

Использование операции then chain

Итак, на первый взгляд Promise может только упростить способ написания обратных вызовов, но по сути суть Promise — это «состояние». лучше, чем передача функции обратного вызова. Гораздо проще и гибче. Итак, правильный сценарий использования промисов таков:

p.then((data) => {
    console.log(data);
})
.then((data) => {
    console.log(data);
})
.then((data) => {
    console.log(data);
});


Использование отклонения:

Установите состояние промиса на отклоненное, чтобы мы могли поймать его в then и выполнить обратный вызов для условия «сбой». См. код ниже.

    let p = new Promise((resolve, reject) => {
        //做一些异步操作
      setTimeout(function(){
            var num = Math.ceil(Math.random()*10); //生成1-10的随机数
            if(num<=5){
                resolve(num);
            }
            else{
                reject('数字太大了');
            }
      }, 2000);
    });
    p.then((data) => {
            console.log('resolved',data);
        },(err) => {
            console.log('rejected',err);
        }
    ); 
Затем передаются два параметра, метод then может принимать два параметра, первый соответствует обратному вызову разрешения, а второй соответствует обратному вызову отклонения. Таким образом, мы можем получить данные, передаваемые ими отдельно. Запустите этот код несколько раз, и вы случайным образом получите следующие два результата:
или

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

Мы знаем, что помимо метода then объект Promise имеет еще и метод catch, для чего он нужен? По сути, это то же самое, что и второй параметр then, который используется для указания обратного вызова reject. Использование такое:

p.then((data) => {
    console.log('resolved',data);
}).catch((err) => {
    console.log('rejected',err);
});

Эффект такой же, как написано во втором параметре then. Но у него есть другая функция: при выполнении callback’а resolve (то есть первый параметр в потом выше), если будет выброшено исключение (код неверный), то он не сообщит об ошибке и не заблокирует js, а войдет в метод ловли. См. код ниже:

p.then((data) => {
    console.log('resolved',data);
    console.log(somedata); //此处的somedata未定义
})
.catch((err) => {
    console.log('rejected',err);
});

В обратном вызове разрешения мы console.log(somedata), а переменная somedata не определена. Если мы не используем Promise, код сообщит об ошибке прямо в консоли, когда запустится здесь, и не будет запускаться дальше. Но здесь вы получаете такой результат:


То есть он переходит в метод catch, а причина ошибки передается в параметр Reason. Даже неправильный код не сообщит об ошибке, которая имеет ту же функцию, что и наш оператор try/catch.

Использование всех: тот, кто работает медленнее, выполнит обратный вызов. all получает параметр массива, а значения в нем в итоге вернутся в объект Promise

Метод all Promise предоставляет возможность выполнять асинхронные операции параллельно и выполнять обратные вызовы после выполнения всех асинхронных операций. См. пример ниже:
let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})

let p = Promise.all([Promise1, Promise2, Promise3])

p.then(funciton(){
  // 三个都成功则成功  
}, function(){
  // 只要有失败,则失败 
})

При всем этом вы можете выполнять несколько асинхронных операций параллельно и обрабатывать все возвращаемые данные в одном обратном вызове, разве это не круто?Есть сцена, которая очень подходит для использования этого.Для некоторых приложений с большим количеством игровых материалов, при открытии веб-страницы, предзагружаются различные ресурсы, такие как картинки, flash и различные статические файлы. После того, как все загружено, приступаем к инициализации страницы.

Использование гонки: тот, кто бежит быстрее, выполнит обратный вызов

Сценарии использования гонки: например, мы можем использовать гонку, чтобы установить тайм-аут для асинхронного запроса и выполнить соответствующую операцию после тайм-аута.Код выглядит следующим образом:
 //请求某个图片资源
    function requestImg(){
        var p = new Promise((resolve, reject) => {
            var img = new Image();
            img.onload = function(){
                resolve(img);
            }
            img.src = '图片的路径';
        });
        return p;
    }
    //延时函数,用于给请求计时
    function timeout(){
        var p = new Promise((resolve, reject) => {
            setTimeout(() => {
                reject('图片请求超时');
            }, 5000);
        });
        return p;
    }
    Promise.race([requestImg(), timeout()]).then((data) =>{
        console.log(data);
    }).catch((err) => {
        console.log(err);
    });
Функция requestImg будет запрашивать изображение асинхронно.Я написал адрес как «путь к изображению», поэтому он не должен быть успешно запрошен. Функция тайм-аута — это асинхронная операция с задержкой в ​​5 секунд. Мы помещаем эти две функции, которые возвращают объекты Promise, в гонку, поэтому они будут гоняться.Если запрос изображения будет успешным в течение 5 секунд, затем введите метод then и выполните обычный процесс. Если изображение не было успешно возвращено в течение 5 секунд, то тайм-аут превысит производительность, введет улов и сообщит информацию о «тайм-ауте запроса изображения». Результаты приведены ниже:


Ну, я думаю, что все уже поняли использование, так что давайте напишем собственное обещание.

3. Реализуйте собственное обещание на основе обещания+

Шаг 1: Реализуйте методы обратного вызова для успеха и неудачи

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

class Promise {
    constructor (executor){
        //默认状态是等待状态
        this.status = 'panding';
        this.value = undefined;
        this.reason = undefined;
        //存放成功的回调
        this.onResolvedCallbacks = [];
        //存放失败的回调
        this.onRejectedCallbacks = [];
        let resolve = (data) => {//this指的是实例
            if(this.status === 'pending'){
                this.value = data;
                this.status = "resolved";
                this.onResolvedCallbacks.forEach(fn => fn());
            }
 
        }
        let reject = (reason) => {
            if(this.status === 'pending'){
                this.reason = reason;
                this.status = 'rejected';
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try{//执行时可能会发生异常
            executor(resolve,reject);
        }catch (e){
            reject(e);//promise失败了
        }
       
    }

Спецификация обещания A+ предусматривает, что при возникновении нештатной ошибки выполняется функция отказа.

constructor (executor){
    ......      try{
        executor(resolve,reject);
      }catch(e){
        reject(e);
      }
  }

Шаг 2: затем вызов цепочки методов

Метод then — это самый простой метод обещания. Он возвращает два обратных вызова, успешный обратный вызов и неудачный обратный вызов. Процесс реализации выглядит следующим образом:

    then(onFulFilled, onRejected) {
    if (this.status === 'resolved') { //成功状态的回调
      onFulFilled(this.value);
    }
    if (this.status === 'rejected') {//失败状态的回调
      onRejected(this.reason);
    }
  }

let p = new Promise(function(){
    resolve('我是成功');
})
p.then((data) => {console.log(data);},(err) => {});
p.then((data) => {console.log(data);},(err) => {});
p.then((data) => {console.log(data);},(err) => {});

Возвращаемый результат:

我是成功
我是成功
我是成功

Чтобы добиться такого эффекта, последний код будет перечитываться, мы можем хранить результат каждого вызова resolve в массив, и сохранять массив каждый раз, когда вызывается результат Reject. ЭтоПочему два массива определены выше, и причина для обхода двух массивов в resolve() и reject() соответственно. Поэтому перед вызовом resolve() или reject(), когда мы находимся в состоянии ожидания, мы сохраним результаты нескольких then в массиве, а приведенный выше код будет изменен на:

  then(onFulFilled, onRejected) {
    if (this.status === 'resolved') {
      onFulFilled(this.value);
    }
    if (this.status === 'rejected') {
      onRejected(this.reason);
    }
    // 当前既没有完成 也没有失败
    if (this.status === 'pending') {
      // 存放成功的回调
      this.onResolvedCallbacks.push(() => {
        onFulFilled(this.value);
      });
      // 存放失败的回调
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }

Спецификация Promise A+ предусматривает, что метод then может вызываться в цепочке

В обещаниях результатом цепного вызова является возврат нового обещания.Результат, возвращенный в первом then, независимо от того, успешно оно или нет, будет возвращен в состояние успеха в следующем then, а в первом then Если возникло исключение возникает ошибка, затем она вернется в состояние сбоя в следующем, а затем

Когда цепочка вызовов успешна

Успех цепного вызова вернет значение.Случаев много.Согласно примеру, результаты, которые могут произойти, примерно перечислены. Поэтому напишите отдельный метод для значения, возвращаемого связанным вызовом. В метод передаются четыре параметра, а именно p2, x, resolve, reject, p2 относится к последнему возвращенному обещанию, x представляет результат, возвращаемый при выполнении обещания, resolve и reject — это методы p2. Тогда код записывается так:

function resolvePromise(p2,x,resolve,reject){
    ....
}

  • Возвращаемый результат не может быть самим собой

var p = new Promise((resovle,reject) => {
    return p;     //返回的结果不能是自己,
})

Никогда не преуспевать и не терпеть неудачу, если возвращаемый результат равен self, поэтому при возврате self должна быть выдана ошибка.

function resolvePromise(p2,x,resolve,reject){
    if(px===x){
        return reject(new TypeError('自己引用自己了'));
    }
    ....
}

  • Результат возврата может быть обещанием

function resolvePromise(promise2,x,resolve,reject){
    //判断x是不是promise
    //规范中规定:我们允许别人乱写,这个代码可以实现我们的promise和别人的promise 进行交互
    if(promise2 === x){//不能自己等待自己完成
        return reject(new TypeError('循环引用'));
    };
    // x是除了null以外的对象或者函数
    if(x !=null && (typeof x === 'object' || typeof x === 'function')){
        let called;//防止成功后调用失败
        try{//防止取then是出现异常  object.defineProperty
            let then = x.then;//取x的then方法 {then:{}}
            if(typeof then === 'function'){//如果then是函数就认为他是promise
                //call第一个参数是this,后面的是成功的回调和失败的回调
                then.call(x,y => {//如果Y是promise就继续递归promise
                    if(called) return;
                    called = true;
                    resolvePromise(promise2,y,resolve,reject)
                },r => { //只要失败了就失败了
                    if(called) return;
                    called = true;
                    reject(r);  
                });
            }else{//then是一个普通对象,就直接成功即可
                resolve(x);
            }
        }catch (e){
            if(called) return;
            called = true;
            reject(e)
        }
    }else{//x = 123 x就是一个普通值 作为下个then成功的参数
        resolve(x)
    }

}

  • Возвращенный результат может быть обычным значением, тогда сразу разрешается (x);
  • Промисы можно вызывать только в случае успеха или неудачи

То есть, когда вызов успешен, вызов больше не может потерпеть неудачу.Если оба вызываются, будет выполнено то, что было вызвано первым. Часть кода такая же, как и выше

Лично я считаю, что это место достаточно извилистое, и его нужно разъяснять поэтапно не спеша.

Согласно принципу спецификации promise A+, promise инкапсулирует ряд встроенных методов в свою собственную структуру.

  • Способы отлова ошибокcatch()
  • Разобрать все методыall()
  • соревнованиеrace()
  • Создайте успешное обещаниеresolve()
  • создать невыполненное обещаниеreject()

Наконец, я прилагаю весь исходный код для внимательного прочтения всеми.

function resolvePromise(promise2,x,resolve,reject){
    //判断x是不是promise
    //规范中规定:我们允许别人乱写,这个代码可以实现我们的promise和别人的promise 进行交互
    if(promise2 === x){//不能自己等待自己完成
        return reject(new TypeError('循环引用'));
    };
    // x是除了null以外的对象或者函数
    if(x !=null && (typeof x === 'object' || typeof x === 'function')){
        let called;//防止成功后调用失败
        try{//防止取then是出现异常  object.defineProperty
            let then = x.then;//取x的then方法 {then:{}}
            if(typeof then === 'function'){//如果then是函数就认为他是promise
                //call第一个参数是this,后面的是成功的回调和失败的回调
                then.call(x,y => {//如果Y是promise就继续递归promise
                    if(called) return;
                    called = true;
                    resolvePromise(promise2,y,resolve,reject)
                },r => { //只要失败了就失败了
                    if(called) return;
                    called = true;
                    reject(r);  
                });
            }else{//then是一个普通对象,就直接成功即可
                resolve(x);
            }
        }catch (e){
            if(called) return;
            called = true;
            reject(e)
        }
    }else{//x = 123 x就是一个普通值 作为下个then成功的参数
        resolve(x)
    }

}

class Promise {
    constructor (executor){
        //默认状态是等待状态
        this.status = 'panding';
        this.value = undefined;
        this.reason = undefined;
        //存放成功的回调
        this.onResolvedCallbacks = [];
        //存放失败的回调
        this.onRejectedCallbacks = [];
        let resolve = (data) => {//this指的是实例
            if(this.status === 'pending'){
                this.value = data;
                this.status = "resolved";
                this.onResolvedCallbacks.forEach(fn => fn());
            }
 
        }
        let reject = (reason) => {
            if(this.status === 'pending'){
                this.reason = reason;
                this.status = 'rejected';
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try{//执行时可能会发生异常
            executor(resolve,reject);
        }catch (e){
            reject(e);//promise失败了
        }
       
    }
    then(onFuiFilled,onRejected){ 
        //防止值得穿透 
        onFuiFilled = typeof onFuiFilled === 'function' ? onFuiFilled : y => y;
        onRejected = typeof onRejected === 'function' ? onRejected :err => {throw err;}        
        let promise2;//作为下一次then方法的promise
       if(this.status === 'resolved'){
           promise2 = new Promise((resolve,reject) => {
               setTimeout(() => {
                  try{
                        //成功的逻辑 失败的逻辑
                        let x = onFuiFilled(this.value);
                        //看x是不是promise 如果是promise取他的结果 作为promise2成功的的结果
                        //如果返回一个普通值,作为promise2成功的结果
                        //resolvePromise可以解析x和promise2之间的关系
                        //在resolvePromise中传入四个参数,第一个是返回的promise,第二个是返回的结果,第三个和第四个分别是resolve()和reject()的方法。
                        resolvePromise(promise2,x,resolve,reject)
                  }catch(e){
                        reject(e);
                  } 
               },0)
           }); 
       } 
       if(this.status === 'rejected'){
            promise2 = new Promise((resolve,reject) => {
                setTimeout(() => {
                    try{
                        let x = onRejected(this.reason);
                        //在resolvePromise中传入四个参数,第一个是返回的promise,第二个是返回的结果,第三个和第四个分别是resolve()和reject()的方法。
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e);
                    }
                },0)

            });
       }
       //当前既没有完成也没有失败
       if(this.status === 'pending'){
           promise2 = new Promise((resolve,reject) => {
               //把成功的函数一个个存放到成功回调函数数组中
                this.onResolvedCallbacks.push( () =>{
                    setTimeout(() => {
                        try{
                            let x = onFuiFilled(this.value);
                            resolvePromise(promise2,x,resolve,reject);
                        }catch(e){
                            reject(e);
                        }
                    },0)
                });
                //把失败的函数一个个存放到失败回调函数数组中
                this.onRejectedCallbacks.push( ()=>{
                    setTimeout(() => {
                        try{
                            let x = onRejected(this.reason);
                            resolvePromise(promise2,x,resolve,reject)
                        }catch(e){
                            reject(e)
                        }
                    },0)
                })
           })
       }
       return promise2;//调用then后返回一个新的promise
    }
    catch (onRejected) {
        // catch 方法就是then方法没有成功的简写
        return this.then(null, onRejected);
    }
}
Promise.all = function (promises) {
    //promises是一个promise的数组
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最终返回值的结果
        let i = 0; // 表示成功了多少次
        function processData(index, data) {
            arr[index] = data;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (data) {
                processData(i, data)
            }, reject)
        }
    })
}
// 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
Promise.race = function (promises) {
    return new Promise((resolve, reject) => {
        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
}
// 生成一个成功的promise
Promise.resolve = function(value){
    return new Promise((resolve,reject) => resolve(value);
}
// 生成一个失败的promise
Promise.reject = function(reason){
    return new Promise((resolve,reject) => reject(reason));
}
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise( (resolve, reject) =>  {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}
module.exports = Promise;

Что касается резюме этой спецификации обещания A +, определенно будет много недостатков.Вы можете высказать ценные мнения или предложения, и я надеюсь помочь вам получить некоторые знания из этого!