связать
Понимание красоты асинхронности: 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. Периодическая статья, которая делится процессом обучения со всеми. С нетерпением жду этого! ! !