Понимание красоты асинхронности: обещание и асинхронное ожидание (2)

внешний интерфейс исходный код JavaScript Promise

связать

Основа предварительного нагрева: привет, асинхронное программирование на JavaScript — понимаем красоту асинхронного JavaScript

Понимание красоты асинхронности: Promise и async await (1)

Прочитав предыдущее базовое объяснение промисов, я думаю, что у каждого есть определенное понимание основного использования и идей промисов. (Это обещание)

Давайте посмотрим, как это работает

Объедините исходный код и проанализируйте общие реализации других, чтобы понять

Ниже приведен общий исходный код, реализованный другими (просто взгляд)

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise(callback) {
    this.status = PENDING;
    this.value = null;
    this.defferd = [];
    setTimeout(callback.bind(this, this.resolve.bind(this), this.reject.bind(this)), 0);
}
    Promise.prototype = {
        constructor: Promise,
        resolve: function (result) {
            this.status = FULFILLED;
            this.value = result;
            this.done();
        },
        reject: function (error) {
            this.status = REJECTED;
            this.value = error;
        },
        handle: function (fn) {
            if (!fn) {
                return;
            }
            var value = this.value;
            var t = this.status;
            var p;
            if (t == PENDING) {
                this.defferd.push(fn);
            } else {
                if (t == FULFILLED && typeof fn.onfulfiled == 'function') {
                    p = fn.onfulfiled(value);
                }
                if (t == REJECTED && typeof fn.onrejected == 'function') {
                    p = fn.onrejected(value);
                }
                var promise = fn.promise;
                if (promise) {
                    if (p && p.constructor == Promise) {
                        p.defferd = promise.defferd;
                    } else {
                        p = this;
                        p.defferd = promise.defferd;
                        this.done();
                    }
                }
            }
        },
        done: function () {
            var status = this.status;
            if (status == PENDING) {
                return;
            }
            var defferd = this.defferd;
            for (var i = 0; i < defferd.length; i++) {
                this.handle(defferd[i]);
            }
        },
        then: function (success, fail) {
            var o = {
                onfulfiled: success,
                onrejected: fail
            };
        var status = this.status;
        o.promise = new this.constructor(function () {});
        if (status == PENDING) {
            this.defferd.push(o);
        } else if (status == FULFILLED || status == REJECTED) {
            this.handle(o);
        }
        return o.promise;
        }
    };

Это реализация исходного кода распространенного промиса в интернете, я его проанализирую. (Кто-то должен спросить, почему бы не реализовать его самостоятельно? Решение: сэкономить время, онлайн слишком много, суть в том, чтобы понять идею)

Без лишних слов, давайте начнем

Давайте сначала разберемся, что мы можем сделать с реализацией.

первый :

let promsie = new Promise((resolve,reject)=>{
    doSomething()
})
根据这个构造函数,我们需要实现两个方法,resolve、reject方法。

second :

promise.then(res=>{
    doSomethingRes();
},rej=>{
    doSomenthingRej()
})
我们要实现一个then方法,可以去根据不同状态来执行不同的函数。

Мы выделили два самых основных.

Без лишних слов приступайте к анализу кода.

Анализ кода нельзя пропустить в комментариях к контенту! ! !

Первый абзац конструктора и настройки состояния

// 首先声明三个状态,
// 状态的就是上一节说的:事情是进行中、已完成、失败了。
// 这三种状态遵循着PENDING->FULEFILLED 或者PENDING->REJECTED
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
// Promise构造函数接收u一个回调函数
function Promise(callback) {
    // 新new出来的实例的status一定是PENDING.
    this.status = PENDING;
    // value是指当你事情完成、失败后内部保存的值
    // 用法是resolve(42) 在then函数的res=>{dosomething()res就是42
    }
    this.value = null;
    // defferd 字面意思推迟、是个数组,存放这个promise以后要执行的事件
    // 类比发布订阅模式(观察者模式)。存放观察者在被观察者身上订阅的事件列表
    // defferd内的事件存放着日后观察者要执行的事件。
    this.defferd = [];
    // setTimeout异步的去执行new 一个promise实例内执行的任务,不去阻塞主线程。
    //这一段代码我有一点疑惑,new promise实例时,callback的执行并不是异步的。
    // 而这里选择异步的并不是很合理。
    // bind函数的作用。callback的参数如何指定?通过bind方法将函数函数柯里化(Currying 和一个NBA球星的名字一样很好记)
    // 而且绑定函数的this执行。函数内的this都执行这个new 出来的promise实例
    // 去指定函数执行时的参数,将resolve方法与reject方法强制做为callback的参数。
    // 所以我们写的回调函数,参数怎么命名都可以执行到对应的resolve与reject方法
    setTimeout(callback.bind(this, this.resolve.bind(this), this.reject.bind(this)), 0);
}

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

К этому моменту партнеры узнали, что мы делаем, когда создаем новый конструктор.

1: Определите состояние экземпляра обещания со значением PENDING.

2: Определите пространство для хранения значений для экземпляра обещания.

3: Настройте список выпуска и выпустите события в нем в более позднее указанное время.

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

Анализ второго абзаца Решить Отклонить Метод ТО

Зачем говорить об этих трех методах в первую очередь. Поскольку resolve и reject являются основными методами, невозможно ничего сказать, но то, что должно быть сделано с помощью resolve и reject, должно быть указано в методе then, так что эти три метода тесно связаны.

// 在Promise的原型对象上指定这些方法。
// 这种做法有很大弊端、并且Promise源码也并不是这么做的之后会进行分析
Promise.prototype = {
        // 覆盖式的指定Promise的原型对象会导致constructor属性丢失
        // 在这里进行填补,手动指定Promise原型对象上的constructor属性
        constructor: Promise,
        // resolve方法开始 接收一个结果(可以为空)
        resolve: function (result) {
            //更改状态为FULFILLED。
            this.status = FULFILLED;
            // 将result存放在之前构造函数中提到的存放结果的空间中
            this.value = result;
            // done方法。表示执行完毕(后面会继续将)
            this.done();
        },
        // 与resolve方法类似 不多做解释
        reject: function (error) {
            // 状态更改
            this.status = REJECTED;
            this.value = error;
            // 没有done函数,这块做法很有问题下面会配图解释。
        },
        // then方法开始要好好讲讲
        // success表示状态变成FULFILLED时要执行的函数
        // fail表示状态变成REJECTED时要执行的函数
        then: function (success, fail) {
        // 声明一个对象来存放这些事件。
            var o = {
                onfulfiled: success,
                onrejected: fail
            };
        // 获取当前promise的状态。
        // 这个的意义是,我们对promise实例执行then方法的时候有两种情况
        // 一:promise实例的内容还没有执行完毕。二:promise实例内容已经执行完毕并且状态已经改变。继续下面。
        var status = this.status;
        o.promise = new this.constructor(function () {});
        // 如果状态是PENDING,表示实例内容还未执行完毕。
        // 说明then方法指定在某种状态要执行的事件是未来发生的现在并不执行。
        // 所以将o放入defferd订阅列表中。
        // 这个之前讲过defferd中的内容会在某个条件触发后会执行。
        // 所以当promise实例内容还未完成就要把未来执行的方法放入订阅列表
        if (status == PENDING) {
            this.defferd.push(o);
        // 对应之前说的情况二    
        // 状态变成以下两种情况怎么办?
        // 订阅列表内不应该有当前情况的o。
        // 所以要立即执行当前指定的事件,而且未来的任何情况下这次指定的事件也不会执行。
        //所以不会放入defferd中
        } else if (status == FULFILLED || status == REJECTED) {
           // 执行handle函数
            this.handle(o);
        }
        // then方法存在链式调用,then方法的返回值必须是一个promise对象
        return o.promise;
        }
    };

Здесь мы примерно разобрались с полезностью разрешения, отклонения и методов.

Упомянем, что метод reject не выполняет функцию done, что приведет к следующим ситуациям.

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

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

Как работает третий абзац? Говорите о сделанном и обрабатывайте

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

// 看完done方法的朋友肯定对我这种方式很闹心,保证你们看的热情嘛。哈哈哈哈哈
// 没看done方法的快回去看、快回去看。
// 必须提一下 下面一直说的o是什么? o是在then方法中传入defferd数组中的对象,一下简称为o。
// handle是干嘛的??? 那是用来执行o的。o里面放着我们想要执行的内容
// 大家再回忆以下,handle还在哪里执行了?想起来了吧,当then方法执行时
// 如果状态已经改变了。那么就直接handle(o),执行你要做的事情。
handle: function (fn) {
    // o不存在的???妈耶,咋办呀。那就是defferd中没东西。好吧什么都不做
    if (!fn) {
        return;
    }
    var value = this.value;
    var t = this.status;
    var p;
    // 如果状态为PENDING,表示还没到o要执行的内容。那么不能执行的?
    if (t == PENDING) {
        
        this.defferd.push(fn);
    } else {
        // 这里面很容易看的,状态变成FULFILLED,
        //并且你在给状态是FULFILLED时要做的事情可以执行(函数才能执行呀,你写个字符串不报错了??)
        // 执行咯。这里面大家一看就知道,你then方法里面res=>{doSometthing(res)}
        //这个res就是promise内存放的结果(value)。
        if (t == FULFILLED && typeof fn.onfulfiled == 'function') {
            p = fn.onfulfiled(value);
        }
        // 不多提了。
        if (t == REJECTED && typeof fn.onrejected == 'function') {
            p = fn.onrejected(value);
        }
        // 但是这个p是干什么的????
        // 存放方法的返回值,为了链式调用。实现链式调用的是什么方法?
        // 返回的o.promise. 那么返回的o.promise不存在呢?(当然这是不可能的)
        // 那就没有链式调用。
        // promise中有一个方法,当then函数内的事件(指的这个函数:res=>{})
        // 返回值是一个promise对象时,那么then的返回值就是这个promise对象
        // 链式中的下一个then就会等待这个promise执行完毕。
        // 如果不是promise对象怎么办?那么就执行链条后面的then方法注册的事情。
        var promise = fn.promise;
        if (promise) {
            // 如果你注册的事件执行后的返回值是一个promise对象
            if (p && p.constructor == Promise) {
                // 当前这个p(是个promise对象)可以把o.promise对象订阅列表内的事件拿过来。
                // 串行后,返回的promise继续控制着defferd的内容。
                //理论上讲,按刚才的逻辑来写,每个promise对象内的defferd,都应该只有一个值。
                // 因为串行的链条每个then注册的事件都在上一个then返回的o.promise的defferd内。
                // 那么为什么?defferd要写个数组呢???这是我疑惑的地方但是影响不大
                p.defferd = promise.defferd;
            } else {
               // 如果不是promise呢?那么就把当前的promise对象当作你的返回值
               // 继续继承o.promise里面的defferd
                p = this;
                p.defferd = promise.defferd;
                // 并没有任何承诺,p应该就是一个resolved(reject)状态
                // 直接执行done方法就可以啦。
                this.done();
            }
        }
    }
},
// done方法是在resolve方法(reject为啥没写之前有讲过)中执行的,表示resolve方法执行完毕了。
// 这个完毕是一种明确信号,那就是之前说好的状态变成FULFILLED我要做的那些事情。
// 乡亲们、弟兄们咱们可以被执行了
done: function () {
    // 当然为了保险起见PENDING状态肯定不会执行,return掉。
    // done函数在PENDING状态也不会被调用呀。双重保险嘛
    var status = this.status;
    if (status == PENDING) {
        return;
    }
    // 乡亲们、兄弟们在哪呢?就在你的defferd中。我们的订阅列表内的兄弟们该执行了。
    // 遍历以下我们的defferd对象,谁也别漏下都给我执行了。
    var defferd = this.defferd;
    for (var i = 0; i < defferd.length; i++) {
    // 问题来了乡亲们不是函数是一个对象啊。
    //为啥是对象?在then方法中有defferd.push(o);o是啥?是个对象啊。
    // 那咋执行????需要一个特定来执行o的方法,就是handle。
    // 好了伙伴们可以把文章回到上面一点了。
        this.handle(defferd[i]);
    }
},

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

Дорогая! Подумайте после обучения

Я не знаю, как вы относитесь к распространенной в Интернете реализации исходного кода обещаний, когда видите ее? ? ?

Я скажу тебе, как я себя чувствую

После прочтения исходного кода (извините, мой IQ ограничен, я действительно не могу понять его за короткое время), я чувствую, что исходный код слишком разумен. Я не понимаю, и я думаю, что это разумно. . . . Это не чрезмерное преклонение перед сильным, но вполне разумное. Распространенные реализации в Интернете просто реализуют функции, такие промисы подходят только для людей, которые имеют определенный опыт промисов и соблюдают правила. Почему ты это сказал? ? ?

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

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

Третье: выполнения отказа недостаточно, и это разумное использование решения.

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

Исходный код обещания (определенная версия, номер версии не помню)

Поместите методы разрешения, отклонения, всех, гонки и обработки в конструктор.

Поместите ловушку, а затем цепные методы в прототип.

В качестве доказательства есть картинки, буквальное значение должно быть таким, думаю, я не ошибаюсь. Хорошо изменить состояние и значение промиса. Методы PromiseSet часто используются для установки свойств, инкапсуляции методов, упрощения управления состоянием и повышения отказоустойчивости.

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

Хватит болтать, пей куриный суп

Дорога изучения фронтенда еще очень длинная, а прочитанный (только что прочитанный) исходный код можно пересчитать в полруки. Все еще верьте в настойчивость, и вы станете великим. Все учатся из оператора потока управления, а логика — это всего лишь сложный поток управления (также включающий высококлассные алгоритмы и шаблоны проектирования), и я твердо верю, что смогу добиться успеха! ! ! Давайте работать вместе каждый front-end er (мальчик и девочка). Поэтому непонимание и непонимание на уровне исходного кода можно списать на то, что меньше видишь, меньше думаешь и меньше понимаешь. (Это не имеет ничего общего с вашим IQ)

следующее уведомление

Следующая статья является конечной точкой понимания красоты асинхронности. Красота асинхронности заключается в этой волшебной мысли. Хватай разум за хвост, не связывайся технологиями, хе-хе.

Собираюсь начать новый курс. Субъект должен изучать исходный код vue-router. Периодическая статья, которая делится процессом обучения со всеми. С нетерпением жду этого! ! !

Я новый выпускник. Недавно я вел публичный аккаунт со своими друзьями. Контент о ямах, на которые мы наступили при переходе от новичков к развитию. Это был отчет о нашем пошаговом обучении. .Если вам интересно, то можете обратить на это внимание. , давай вместе~

个人公众号:IT面试填坑小分队