Сяо Шао учит вас играть с исходным кодом обещания

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

Предисловие: Всем привет, меня зовут Шао Вэйру, всем нравится называть меня Сяо Шао, но я попал в яму программистов из-за своих увлечений.Купил первый вб в ​​универе и сам выучил вб, и я наладил отношения с программированием. Неразрывная связь, затем самостоятельно выученный простой язык для написания игровых пособий и программного обеспечения для торговли, и я вошел в область переднего плана. Я видел, как многие друзья пишут статьи, чтобы поделиться ими, и я также сделал одну, чтобы играть. Следующие статьи являются чисто личным пониманием, которое удобно для записи и обучения.Должны быть недоразумения или недоразумения.Намерение состоит в том, чтобы стоять на плечах предшественников, делиться личным популярным пониманием технологии и расти вместе!

В будущем я продолжу обновлять аспект javascript и постараюсь максимально подробно описать систему путей обучения javascript.
Включая es6, angular, react, vue, nodejs, koa, express, официальный аккаунт и т. д., обычно используемые во внешнем интерфейсе.
Он будет написан от мелкого к глубокому, от начала к началу, я надеюсь, что каждый сможет что-то получить, и я надеюсь, что все обратят на меня внимание~

Адрес источника:GitHub.com/IA ms Nobody/prom…
Список статей:Талант /user/372040…

Автор: Шао Вейру.
Email: 166661688@qq.com
Wechat: 166661688
github: github.com/iamswr/


Как однопоточный язык, JavaScript также характеризуется своими недостатками. Характеристика заключается в том, что ему не нужно иметь дело с занятием ресурсов и конфликтами, вызванными многопоточностью. Недостаток в том, что в то же время только одно может быть сделано, поэтому будет проблема, передача по сети Существует задержка.Например, когда A отправляет сообщение на сервер B, когда сервер B не вернул сообщение A, тогда A всегда будет ждать, чтобы получить сообщение , что приведет к зависанию страницы, что мне делать? Как говорится, программисты меняют мир, поэтому и появилось понятие асинхронности.Я разделю следующие пункты, чтобы описать мое понимание фронтенд-асинхронности:

  1. callback
  2. Замыкания, функции высшего порядка
  3. promise
  4. генератор (адрес статьи:nuggets.capable/post/684490…)
  5. async/await (адрес статьи:nuggets.capable/post/684490…)

1.обратный вызов

Функция обратного вызова считается функцией высокого уровня, функцией высокого уровня, которая передается в качестве параметра другой функции (называемой здесь «otherFunction»), и функция обратного вызова будет вызываться (или выполняться) внутри функции otherFunction. Суть callback-функции — это паттерн (паттерн для решения общих задач), поэтому callback-функцию также называют паттерном обратного вызова.

Кажется запутанным, что такое функция обратного вызова? каков эффект? Мое понимание функции обратного вызова в реальном смысле внезапно заставило меня осознать, что когда я изучал базовый исходный код jQuery, мы видели следующий код:

<img src='../a.jpg'></img>
<img src='../b.jpg'></img>
<img src='../c.jpg'></img>

$("img").attr("title",function(index,attr){
    console.log(index) // 依次返回0 1 2
});

Во втором параметре метода attr передается функция, а функция будет получена в свою очередь$('img')Объект DOM соответствует индексу и атрибуту Мы можем написать нужную нам бизнес-логику в этой функции, так в чем же польза от этого?

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

Предположим, теперь у нас есть требование написать метод для оценки типа.Общие методы следующие:

 - typeOf // 简单的数据类型判断,栈区
 - instanceof // 复杂的数据类型,堆区
 - constructor // 复杂的数据类型,主要是用在继承的改写指向的构造函数,很少用于判断类型
 - Object.prototype.toString.call() // 绝大多数库底层都是使用该方式,返回值如[object String]

Во-первых, мы пишем метод isType

function isType(content,type,fn){
    // 类型判断
    let t = Object.prototype.toString
                              .call(content)
                              .replace(/\[object\s|\]/g,'')
                              
    // 判断完成后,执行传入的callback函数
    fn(type,t)
}

Теперь нам нужно определить тип значения, затем получить тип и, наконец, сделать то, что нам нужно сделать.

isType('hello swr','String',function(type,t){ // 作为参数传入的函数,接收isType函数内的fn中type和t这两个参数
    console.log(type === t) // true
})

Сначала выполните функцию isType, затем выполните код внутри функции isType, fn(type,t) в isType, здесь метод fn фактически является третьим параметром, который мы передали, то есть в console.log( type = == t), а t — это t в функции isType, вы можете немного ощутить весь процесс и почти понять, что делает функция обратного вызова~

Тогда возникает проблема: например, когда мы используем node.js, когда мы выполняем операции чтения файла, что произойдет, когда значение, которое мы хотим получить, является вложенной зависимостью?

目录结构:
- iamswr
  - A.txt
  - B.txt
  - C.txt
  
  其中
  A.txt文件里的内容为字符串B.txt
  B.txt文件里的内容为字符串C.txt
  C.txt文件里的内容为字符串'hello swr'
  
  那么当我们想获取到'hello swr',会遇到什么问题呢?请看下面的代码
  let fs = require('fs')
  fs.readFile('A.txt','utf8',function(err,data){ // 此时回调函数data值为'B.txt'
      fs.readFile(data,'utf8',function(err,data){ // 此时回调函数data值为'C.txt'
          fs.readFile(data,'utf8',function(err,data){
            console.log(data) // 'hello swr'
          })
      })
  })

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


2. Замыкания, функции высшего порядка

Я лично понимаю, что замыкание на самом деле является функцией, поэтому технология замыкания также является своего рода функциональной технологией; функции могут делать почти все, что может делать замыкание. хранить значения этих переменных в памяти.

1. Охватывающая область

В javascript, если на объект нет ссылки, то объект будет переработан GC, в противном случае он останется в памяти, поэтому использование этой функции с использованием замыканий имеет следующие преимущества: закрытая область, эффект сохранения Область, область цепь.

Не загрязняйте глобальные переменные. Когда команда сотрудничает, например, Big A, библиотека jQuery инкапсулируется, и в библиотеке jQuery имеется большое количество переменных. Если замыкания не используются, переменные в библиотеке jQuery будут загрязнять весь проект, даже с другими членами команды. Переменные конфликтуют

外部无法获取闭包内的变量,封闭了作用域
(function(){
    var str = 'hello swr'
    console.log(str) // 'hello swr'
})()

console.log(str) // 报错

我们用原生js来写代码的时候,会存在一个问题,
比如有5个button标签
var btns = document.getElementsByTagName('button');
for(var i=0; i< btns.length; i++){   
   var btn = btns[i];        
   btn.onclick = function () {            
     alert('点击了第' + i + '个按钮');       
   }
}
无论我们点击哪个button,都是弹出'点击了第5个按钮',
因为btn.onclick事件是异步触发的,当事件被触发时,
for循环早已经结束,此时变量I的值已经是5,
所有onclick事件函数从内到外查找变量i时,查找到的值总是5。

可以通过封闭作用域把每次循环的i值都封闭起来,
当时间函数顺着作用域链从内到外查找变量i时,
会先找到被封闭在闭包环境中的i,
如果有5个按钮, 则i的值就是0,1,2,3,4
var btns = document.getElementsByTagName('button');
for(var i=0; i< btns.length; i++){   
    (function (i) {        
       var btn = btns[i];        
       btn.onclick = function () {            
         alert('点击了第' + i + '个按钮');       
       }    
     })(i);
}

2. Цепочка областей действия

Мы знаем, что до es6 говорили, что область видимости есть только у функций, а когда появился es6, появилось заявление об области видимости на уровне блоков, например:

(function person(){
    var name = '邵威儒'
    console.log(name) // '邵威儒'
})()

console.log(name) // 报错

За пределами функции внутреннее имя не может быть доступен, что имеет объем. В ES6 появляется новая концепция, которая является объемом уровня блока

{
    let name = '邵威儒'
    console.log(name) // '邵威儒'
}

console.log(name) // 报错

Эффект тот же, что и при закрытии.

3. Сохраните область

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

function plus(num){
    ++num
    return function(){
        console.log(num)
    }
}

let toPlus = plus(5)
此时toPlus实际上为
function(){
    console.log(num)
}
而这个num实际上就是plus函数内作用域的num,此时我们无法从外部修改num,而且把plus函数内的数据隐藏化,将数据绑定在toPlus上运行。

Проблемы, возникающие при фактической разработке

Например, мы столкнемся с проблемой в реальной разработке, то есть функция будет выполняться после завершения нескольких асинхронных исполнений, что нам делать в этой ситуации?

Обычно на ум приходит следующий подход

let fs = require('fs')
let arr = []

fs.readFile('./a.txt','utf8',function(err,data){
    arr.push(data) // 假设data为'hello'
})

fs.readFile('./b.txt','utf8',function(err,data){
    arr.push(data) // 假设data为'swr'
})

console.log(arr) // 我们希望打印出来是['hello','swr']或['swr','hello'],但是打印出来的却是[]
这是为什么呢?
是因为javascript执行原理,是先执行同步,再执行异步的,而fs.readFile方法属于异步方法,所以还没执行完毕,就已经执行了console.log(arr)了

Для такого рода сбора, который не зависит от вложенности, мы называем его «синхронным» сбором Синхронизация, которая является синхронной, а не асинхронной, особенно данные этого асинхронного запроса, получаются в разном порядке времени, так как мы можем достичь "синхронизация" "Понятно?

let fs = require('fs')

function after(times,callback){
    let arr = []
    return function(data){
        arr.push(data)
        if(--times === 0){
            callback(arr)
        }
    }
}

let fn = after(2,function(arr){
    console.log(arr) // 当fn执行两次后,则会执行该回调函数
})

fs.readFile('./a.txt','utf8',function(err,data){
    fn(data) // 假设data为'hello'
})

fs.readFile('./b.txt','uft8',function(err,data)=>{
    fn(data) // 假设data为'swr'
})

最终当2个fs.readFile读取完毕后,执行了fn()达到2次时,则会打印出['hello','swr']或者['swr','hello']

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

let fs = require('fs')

let event = {
    arr:[], // 存需要执行的函数
    result:[], // 存结果
    on(fn){ // 订阅
        this.arr.push(fn)
    },
    emit(data){ // 发布
        this.result.push(data)
        this.arr.forEach(fn=>fn(this.result))
    }
}

event.on(function(data){
    if(data.length === 2){
        console.log(data) // ['hello','swr'] 或者 ['swr','hello']
    }
})

fs.readFile('./a.txt','utf8',(err,data)=>{
    event.emit(data) // data为'hello'
})

fs.readFile('./b.txt','utf8',(err,data)=>{
    event.emit(data) // data为'swr'
})

当两个fs.readFile读取完成,并且在其回调函数内执行了event.emit,最终会打印出['hello','swr'] 或者 ['swr','hello']

3. Обещание

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

Прежде всего, как вы понимаете обещания? Я видел относительно легкую для понимания небольшую историю на Zhihu, вы можете прочитать ее,zhuanlan.zhihu.com/p/19622332

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

Концовка А: Успешно получен (восстановлен) прогноз погоды, солнечно :) Сын успешно получил прогноз погоды, небо чистое и светит солнце! Обещание было выполнено, и папа решил начать подготовку к воскресной рыбалке.

Концовка B: также успешно получил прогноз погоды, дождливый день :( Сыну удалось узнать прогноз погоды, но были только темные тучи и собирался дождь. Обещание было выполнено, только папа решил остаться дома из-за плохой погоды.

Концовка C: Не могу получить прогноз погоды :-/ Когда что-то пошло не так, мой сын не мог получить прогноз погоды, потому что туман был настолько густым, что он не мог видеть его, даже стоя на холме. Сын не смог сдержать обещание, данное им при уходе, обещание было отклонено! Папа решил остаться, и это не стоило риска.

Некоторые свойства промисов

Сначала нам нужно понять спецификацию PromiseA+.promisesaplus.com/

  • У Promise есть проблемы с совместимостью, он поддерживается по умолчанию в среде узла, вы также можете скачать соответствующие плагины для решения проблем совместимости.
  • Существует три состояния обещания: состояние ожидания в ожидании / успешное состояние разрешено / состояние отказа отклонено
  • Состояние обещания может быть преобразовано из ожидание -> решено или ожидание -> отклонено, но решено не может быть преобразовано в отклонено/ожидание, отклонено не может быть преобразовано в решено/ожидание, короче говоря, состояние изменится только один раз
// Promise构造函数的第一个参数为executor
let promise = new Promise(function(resolve,reject){
    console.log('我是会被立即执行的哟')
})

// promise的实例都有then方法
promise.then(()=>{ // 成功的回调
    
},()=>{ // 失败的回调
    
})
  • В новом исполнителе по умолчанию будет выполняться автоматически, когда
  • Каждый экземпляр обещания имеет метод then
  • В методе then есть два параметра, а именно успешная функция обратного вызова и неудачная функция обратного вызова.
// 默认时为pending态,既不会走成功的回调也不会走失败的回调
promise.then(()=>{
    console.log('success1')
},()=>{
    console.log('error1')
})

console.log('2')

在这段代码中,只会打印出'2',因为promise一直处于pending态,不会走then后的回调函数
let promise = new Promise(function(resolve,reject){
    console.log('1')
    resolve() // 更改pending状态为resolved
})

promise.then(()=>{
    console.log('success1')
},()=>{
    console.log('error1')
})

console.log('2')

此时输出顺序为'1' -> '2' -> 'success1'
  • Метод then является асинхронным и относится к микрозадачам, как видно из приведенного выше примера, сначала выполняется синхронный код, а затем выполняется асинхронный код.
let promise = new Promise(function(resolve,reject){
    console.log('1')
    setTimeout(()=>{ // 异步行为
        resolve() // 更改状态为成功
    },1000)
})

promise.then(()=>{
    console.log("success1")
})

promise.then(()=>{
    console.log('success2')
})

console.log("2")

此时输出顺序为'1' -> '2' -> 'success1' -> 'success2'
  • Экземпляр одного и того же промиса может быть затем несколько раз, все методы успеха будут вызываться, когда он будет успешным, и все методы отказа будут вызываться, когда он терпит неудачу.
  • Асинхронное поведение может поддерживаться в новом промисе
let promise = new Promise(function(resolve,reject){
    throw new Error('出错了') // 抛出错误
})

promise.then(()=>{
    console.log('success1')
},()=>{
    console.log('error1')
})

此时输出为 'error1'
  • Если обнаружена ошибка, он войдет в состояние отказа

Реализовать обещание

Следующая часть кода и часть реализации исходного кода должны быть объединены, чтобы увидеть

// ----- 代码部分
// 1.executor默认在new的时候会自动执行
// 成功和失败的视乎可以传递参数
let promise = new Promise((resolve,reject)=>{ // 6.resolve、reject函数对应源码实现部分的resolve、reject函数
    resolve('hello swr') // 11.执行resolve
})

// 7.Promise的实例都有then方法
promise.then((data)=>{ // 8.成功的回调函数
    
},(err)=>{ // 9.失败的回调函数
    
})
// ----- 源码实现部分
// 2.声明一个Promise构造函数
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined // 12.因为value和reason值需要在Promise实例方法then中使用,所以把这两个值,赋给new出来的实例
    function resolve(value){ // 3.声明一个resolve函数
        self.value = value // 13.当调用了resolve并且传参数时,则把这value值赋予self.value
    }
    
    function reject(reason){ // 4.声明一个reject函数
        self.reason = reason // 13.当调用了reject并且传参数时,则把这reason值赋予self.reason
    }
    executor(resolve,reject) // 5.把resolve、reject函数传到executor
}

// 因为Promise的实例都有then方法,那么意味着then方法是在Promise的原型对象中的方法
// 10.对应上面成功的回调函数onFulfilled以及失败的回调函数onRejected
Promise.prototype.then = function(onFulfilled,onRejected){
    
}

module.exports = Promise // 把Promise暴露出去

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

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve('hello swr') // 5.暂时忽略此行
    resolve('看看同时执行resolve和reject会发生什么?')  // 5.此行执行resovle
    reject('看看同时执行resolve和reject会发生什么?') // 5.此行执行reject
})

promise.then((data)=>{
    console.log('success:' + data) // 5.当调用了resolve函数,则输出success:hello swr
},(err)=>{
    
})
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending' // 1.在内部维护一个status状态
    function resolve(value){
        self.value = value 
        self.status = 'resolved' // 2.当调用了resolve时,更改状态为resolved
    }
    
    function reject(reason){
        self.reason = reason 
        self.status = 'rejected' // 2.当调用了reject时,更改状态为rejected
    }
    executor(resolve,reject)
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    // 3.当我们在then中,执行了成功或者失败的回调函数时,首先要判断目前处于什么状态
    if(self.status === 'resolved'){
        onFulfilled(self.value) // 4.当调用了resolve函数后,会执行成功的回调函数,并且把resolve中传递的值,传递给成功的回调函数
    }
    
    if(self.status === 'rejected'){
        onRejected(self.reason) // 4.当调用了reject函数后,会执行成功的回调函数,并且把reject中传递的值,传递给失败的回调函数
    }
}

module.exports = Promise

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

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve('看看同时执行resolve和reject会发生什么?') // 1. 此时执行resolve和reject
    reject('看看同时执行resolve和reject会发生什么?') // 3.此时即使调用reject,因为resolve已经调用了一次,从pending更改为resolve,所以在第一次调用后,多次调用也不会生效
    
    // 4.以上resolve、reject暂时忽略掉,我们考虑一个情况,当promise抛出错误时,怎么去处理呢?
    throw new Error('出错啦')
})

promise.then((data)=>{
    console.log('success:' + data)
},(err)=>{
    
})
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    function resolve(value){
        if(self.status === 'pending'){ // 2.此时新增一个状态判断,当状态为pending的时候才能执行
            self.value = value 
            self.status = 'resolved'
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ // 2.此时新增一个状态判断,当状态为pending的时候才能执行
            self.reason = reason 
            self.status = 'rejected'
        }
    }
    
    // 5.当我们在执行executor时,内部抛出错误的时候,可以利用try catch来处理这个问题
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    if(self.status === 'resolved'){
        onFulfilled(self.value) 
    }
    
    if(self.status === 'rejected'){
        onRejected(self.reason) 
    }
}

module.exports = Promise

Таким образом, мы решаем несколько вызовов, распознаем только первое изменение состояния и используем try catch для обработки, когда выдается ошибка, затем давайте подумаем об этом, в настоящее время мы все новые Promise, а затем вызываем then. Кажется, быть не проблема со всем этим процессом, но теперь возникла проблема, если разрешение или отклонение находится в setTimeout(()=>{resolve()}, 3000) в это время, то есть в асинхронном режиме, когда мы создаем новый one Когда используется Promise, асинхронный код не будет выполняться немедленно, а функция promise.then будет выполняться напрямую. В это время, поскольку состояние self.status все еще находится в состоянии ожидания, разрешение или отклонение не будет выполнено. выполняется синхронный код. При выполнении асинхронного кода, когда состояние изменяется на разрешенное или отклоненное, метод then был выполнен, и метод then не будет выполняться снова, так что же нам делать в это время?

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

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

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    setTimeout(()=>{ // 1.此时resolve处于异步
        resolve('hello swr')
    },3000)
})

promise.then((data)=>{ // 多个then
    console.log('success1:' + data)
},(err)=>{
    
})
promise.then((data)=>{ // 多个then
    console.log('success2:' + data)
},(err)=>{
    
})
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    self.onResolvedCallbacks = [] // 2.可能new Promise中会有异步的操作,此时我们把异步操作时,执行的then函数的成功回调,统一保存在该数组中
    self.onRejectedCallbacks = [] // 2.可能new Promise中会有异步的操作,此时我们把异步操作时,执行的then函数的失败回调,统一保存在该数组中
    function resolve(value){
        if(self.status === 'pending'){ 
            self.value = value 
            self.status = 'resolved'
            // 4.当调用resolve时,把该数组中存放的成功回调都执行一遍,如果是异步,则会把成功的回调都存到该数组里了,如果是异步,则没存到。
            self.onResolvedCallbacks.forEach(fn=>fn())
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ 
            self.reason = reason 
            self.status = 'rejected'
            // 4.当调用reject时,把该数组中存放的失败回调都执行一遍,如果是异步,则会把成功的回调都存到该数组里了,如果是异步,则没存到。
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }
    
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    if(self.status === 'resolved'){
        onFulfilled(self.value) 
    }
    
    if(self.status === 'rejected'){
        onRejected(self.reason) 
    }
    
    // 3.当new Promise中有resolve、reject处于异步中,执行then的时候,状态为pending,
    if(self.status === 'pending'){
        self.onResolvedCallbacks.push(()=>{
            onFulfilled(self.value)
        }) // 3. 把成功的回调函数,存到该数组中,这样写的好处,就是把参数传进去,不需要将来遍历onResolvedCallbacks时,再传参
        self.onRejectedCallbacks.push(()=>{
            onRejected(self.reason)
        }) // 3. 把失败的回调函数,存到该数组中,这样写的好处,就是把参数传进去,不需要将来遍历onRejectedCallbacks时,再传参
    }
}

module.exports = Promise

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


Цепочка обещаний

На самом деле ядро ​​Promise лежит в цепочках вызовов Promise в основном решает две проблемы:

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

Прежде всего, как решить ад обратного вызова? Итак, давайте посмотрим на следующий код и изменим его на promise.

// 回调函数
let fs = require('fs')
fs.readFile('./a.txt','utf8',(err,data)=>{ // 往fs.readFile方法传递了第三个为函数的参数
    if(err){
        console.log(err)
        return
    }
    console.log(data)
})

// 改写为Promise
let fs = require('fs')
function read(filePath,encoding){
    return new Promise((resolve,reject)=>{
        fs.readFile(filePath,encoding,(err,data)=>{
            if(err) reject(err)
            resolve()
        })
    })
}

read('./a.txt','utf8').then((data)=>{ // 在这里则不再需要传回调函数进去,而是采用then来达到链式调用
    console.log(data)
},(err)=>{
    console.log(err)
})

// 这样看好像Promise也没什么优势,那么接下来我们对比一下
// 假设有3个文件
// - 1.txt    文本内容为'2.txt'
// - 2.txt    文本内容为'3.txt'
// - 3.txt    文本内容为'hello swr'

// 用回调函数
fs.readFile('./1.txt','utf8',(err,data)=>{
    fs.readFile(data,'utf8',(err,data)=>{
        fs.readFile(data,'utf8',(err,data)=>{
            console.log(data) // hello swr
        })
    })
})

// 用Promise
read('./1.txt','utf8')
.then((data)=>{
    // 1.如果一个promise执行完后,返回的还是一个promise,
    //   会把这个promise的执行结果会传递给下一次then中
    return read(data,'utf8')
})
.then((data)=>{
    return read(data,'utf8')
})
.then((data)=>{
    // 2.如果在then中返回的不是一个promise,
    //   而是一个普通值,会将这个普通值作为下次then的成功的结果
    return data.split('').reverse().join('')
})
.then((data)=>{
    console.log(data) // rws olleh
    // 3.如果当前then中失败了,会走下一个then的失败回调
    throw new Error('出错')
})
.then(null,(err)=>{
    console.log(err) // Error:出错   报错了
    // 4.如果在then中不返回值,虽然没有显式返回,
    //   但是默认是返回undefined,是属于普通值,依然会把这个普通值传到
    //   下一个then的成功回调中
})
.then((data)=>{
    console.log(data) // undefined
})

Из приведенного выше видно, что Promise переписывает код для лучшего чтения и поддержки, из чего можно сделать вывод о Promise:

  • 1. Если обещание выполнено и обещание возвращено, результат выполнения обещания будет передан следующему затем
  • 2. Если в then возвращается не обещание, а обычное значение, то это обычное значение будет использоваться как успешный результат следующего then
  • 3. Если текущий затем терпит неудачу, он переходит к следующему обратному вызову сбоя.
  • 4. Если в then не возвращается никакого значения, несмотря на отсутствие явного возврата, по умолчанию возвращается неопределенное значение, которое является общим значением, и это общее значение все равно будет передано обратному вызову успеха следующего then.
// 如果在then中抛出错误,会怎样呢?
// 情景一,会被下一个then中的失败回调捕获
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})
.then(null,(err)=>{
    console.log(err) // Error:出错了   报错
})

// 情景二,如果没有被失败的回调捕获,抛出错误最终会变成异常
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})

// 情景三,如果没有被失败的回调捕获,那么最终会被catch捕获到
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})
.then((data)=>{
    
})
.catch((err)=>{
    console.log(err) // Error:出错了   报错
})

// 情景四,如果被失败的回调捕获了,那么不会被catch捕获到
read('./1.txt','utf8')
.then((data)=>{
    throw new Error('出错了')
})
.then(null,(err)=>{
    console.log(err) // Error:出错了   报错
})
.catch((err)=>{
    console.log(err)  // 不会执行到这里
})
  • 5.catch выполняется только тогда, когда ошибка не обрабатывается
  • 6.Потом можно ничего не писать

Вперемежку с разницей в цепочке вызовов с jquery

Цепной звонок jQuery - это вернуть это после его внутреннего исполнения и вернуть свой собственный объект для достижения цели цепного вызова, поэтому почему обещают не принять этот метод?

Мы можем посмотреть на следующий код и почувствовать его.

let promise = new Promise((resolve,reject)=>{
    resolve() // 执行resolve,使状态从pending变为resolved
})

let promise2 = promise.then(()=>{
    throw new Error() // 抛出错误
    return this // 返回自身
})

// 那么我在promise2中,调then,那么它会执行失败的回调吗?答案是不会的。
// 因为我们不可能让状态既成功又失败的
// promise成功了,如果返回this,那不能走向失败
promise2.then(()=>{
    console.log('来到这里了') 
},()=>{
    console.log('会来到这里吗?')
})

// 此时then中返回自身后,promise2其实就是promise,而我们想达到
// 的是把当前的then返回后,传到下一个then中,但是我们这样返回this,
// 其实会变得很矛盾,因为状态已经从pending变为resolved,不可能又从resolved变成rejected的
// 所以得出结论,返回的必须是一个新的promise,因为promise成功后不能再走失败
// 只能创建一个新的promise再执行业务逻辑,返回同一个promise的话,就不能既成功又失败
Реализация цепочек вызовов Promise
// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve()
})

// 2.返回的值为promise2 为什么这样规定呢?这是promiseA+规范规定的,我们要遵循
let promise2 = promise.then((data)=>{
    return x // 1.then中的返回值x可能是普通值也可能是promise,并且传给下一个then
}).then((data)=>{
    console.log(data) // x的值
})
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    self.onResolvedCallbacks = []  
    self.onRejectedCallbacks = []
    function resolve(value){
        if(self.status === 'pending'){ 
            self.value = value 
            self.status = 'resolved'
            self.onResolvedCallbacks.forEach(fn=>fn())
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ 
            self.reason = reason 
            self.status = 'rejected'
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }
    
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    let self = this
    let promise2 // 3.上面讲promise链式调用时,已经说了返回的是一个新的promise对象,那么我们声明一个新的promise
    
    // 4.那么我们new一个新的promise,并且把以下代码放到promise中
    let promise2 = new Promise((resolve,reject)=>{
        if(self.status === 'resolved'){
            // 7.当执行成功回调的时候,可能会出现异常,那么就把这个异常作为promise2的错误的结果
            try{
                let x = onFulfilled(self.value) // 6.这里的x,就是上面then中执行完返回的结果,我们在这里声明一个x用来接收
                // 8.根据promiseA+规范,我们应该提供一个函数来处理promise2
                //   我个人的理解是,then中不管是成功回调还是失败回调,其返回
                //   值,有可能是promise,也有可能是普通值,也有可能是抛出错误
                //   那么我们就需要一个函数来处理这几种不同的情况
                //   这个函数我们声明为resolvePromise吧
                resolvePromise(promise2,x,resolve,reject)
                // 9. 这里的promise2就是当前的promise2,x则是执行then中成功回调后返回的结果,如果是成功则调promise2的resolve,失败则调reject
            }catch(e){
                reject(e) // 注意:这里的reject是这个promise2的reject
            }
        }
        
        if(self.status === 'rejected'){
            // 同6-7步
            try{
                let x = onRejected(self.reason) 
                // 同8-9
                resolvePromise(promise2,x,resolve,reject)
            }catch(e){
                reject(e)
            }
        }
    
        if(self.status === 'pending'){
            self.onResolvedCallbacks.push(()=>{
                // 同6-7步
                try{
                    let x =  onFulfilled(self.value)
                    // 同8-9
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            }) 
            self.onRejectedCallbacks.push(()=>{
                // 同6-7步
                try{
                    let x = onRejected(self.reason)
                    // 同8-9
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            })
        }
    })
    return promise2 // 5.在jquery中是return this,但是在promise中,则是返回一个新的promise对象
}

module.exports = Promise

Напишите функцию resolvePromise

Далее давайте напишем функцию resolvePromise.Основная часть всего промиса находится здесь

// ----- 代码部分
let promise = new Promise((resolve,reject)=>{
    resolve()
})

let promise2 = promise.then((data)=>{
    return x 
}).then((data)=>{
    console.log(data) 
})

// 2.我们在resolvePromise函数中,在原生情况下,如果传参的时候,promise2和x是同一个对象会发生什么呢?
let promise = new Promise((resolve,reject)=>{
    resolve()
})

let promise2 = promise.then(()=>{
    return promise2 
    // 2.1报错 UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>
    // 报错的意思是,陷入了死循环,那怎么理解呢?
    // promise2的成功或失败是要取决于promise中then的返回结果,而返回的却是promi2自己
    // 这样就陷入死循环了,promise2是依赖于promise的then返回的结果,
    // 而then返回的结果是promise2,而then中的promise2,既不是成功也不是失败,不能自己等于自己
})

// 7.当取一个对象上的属性,可能存在报异常的情况,怎么理解呢?
// 因为这个方法有可能不是自己写的,可能别人搞恶作剧乱写的,看以下代码。
let obj = {}
// 给obj对象定义一个then方法,当我们去obj对象中调用then方法时
// 就会执行里面的get,而get则是抛出异常
Object.defineProperty(obj,'then',{
    get(){
        throw new Error()
    }
})

// 10.为什么要用call呢?解决了什么问题?看一下以下代码
首先我们执行
promise.then(()=>{
    console.log(this) // 此时this是指向该promise的,对象的方法中this是指向这个对象的
})

但是我们在下面通过let then = promise.then,来判断是否promise,是否会异常
当我们执行then时,里面的this还是会指向这个promise吗?答案是不一定的,
因为此时then,如果在全局下执行,指向的可能就是window了,所以为了让this的
指向正确,我们需要通过
then.call(promise),来把then的this指向promise
// ----- 源码实现部分
function Promise(executor){
    let self = this
    self.value = undefined
    self.reason = undefined 
    self.status = 'pending'
    self.onResolvedCallbacks = []  
    self.onRejectedCallbacks = []
    function resolve(value){
        if(self.status === 'pending'){ 
            self.value = value 
            self.status = 'resolved'
            self.onResolvedCallbacks.forEach(fn=>fn())
        }
    }
    
    function reject(reason){
        if(self.status === 'pending'){ 
            self.reason = reason 
            self.status = 'rejected'
            self.onRejectedCallbacks.forEach(fn=>fn())
        }
    }
    
    try{
        executor(resolve,reject)
    }catch(error){
        reject(error)
    }
}

// 1.声明一个resolvePromise函数
// 这个函数非常核心,所有的promise都遵循这个规范,所有的promise可以通用,
/**
 * 
 * @param {*} promise2 then的返回值,返回新的promise
 * @param {*} x then中成功函数或者失败函数的返回值
 * @param {*} resolve promise2的resolve
 * @param {*} reject promise2的reject
 */
function resolvePromise(promise2,x,resolve,reject){
    // 3.从2中我们可以得出,自己不能等于自己
    // 当promise2和x是同一个对象的时候,则走reject
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise'))
    }
    // 4.因为then中的返回值可以为promise,当x为对象或者函数,才有可能返回的是promise
    let called
    if(x !== null && (typeof x === 'object' || typeof x === 'function')){
        // 8.从第7步,可以看出为什么会存在抛出异常的可能,所以使用try catch处理
        try{
            // 6.因为当x为promise的话,是存在then方法的
            // 但是我们取一个对象上的属性,也有可能出现异常,我们可以看一下第7步
            let then = x.then 
            
            // 9.我们为什么在这里用call呢?解决了什么问题呢?可以看上面的第10步
            // x可能还是个promise,那么就让这个promise执行
            // 但是还是存在一个恶作剧的情况,就是{then:{}}
            // 此时需要新增一个判断then是否函数
            if(typeof === 'function'){
                then.call(x,(y)=>{ // y是返回promise后的成功结果
                    // 一开始我们在这里写的是resolve(y),但是考虑到一点
                    // 这个y,有可能还是一个promise,
                    // 也就是说resolve(new Promise(...))
                    // 所以涉及到递归,我们把resolve(y)改成以下
                    
                    // 12.限制既调resolve,也调reject
                    if(called) return
                    called = true
                    
                    resolvePromise(promise2,y,resolve,reject)
                    // 这样的话,代码会一直递归,取到最后一层promise
                    
                    // 11.这里有一种情况,就是不能既调成功也调失败,只能挑一次,
                    // 但是我们前面不是处理过这个情况了吗?
                    // 理论上是这样的,但是我们前面也说了,resolvePromise这个函数
                    // 是所有promise通用的,也可以是别人写的promise,如果别人
                    // 的promise可能既会调resolve也会调reject,那么就会出问题了,所以我们接下来要
                    // 做一下限制,这个我们写在第12步
                    
                },(err)=>{ // err是返回promise后的失败结果
                    if(called) return
                    called = true
                    reject(err)
                })
            }else{
                resolve(x) // 如果then不是函数的话,那么则是普通对象,直接走resolve成功
            }
        }catch(e){ // 当出现异常则直接走reject失败
            if(called) return
            called = true
            reject(e)
        }
    }else{ // 5.x为一个常量,则是走resolve成功
        resolve(x)
    }
}

Promise.prototype.then = function(onFulfilled,onRejected){
    // onFulfilled、onRejected是可选参数
    onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
    onRejected = typeof onRejected === 'function'?onRejected: err=>{throw err}
    let self = this
    let promise2 
    let promise2 = new Promise((resolve,reject)=>{
        if(self.status === 'resolved'){
            // 13.根据promiseA+规范,onFulfilled或onRejected必须
            // 被调用不是当前的上下文,then方法是异步的
            setTimeout(()=>{
                try{
                    let x = onFulfilled(self.value)
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e) 
                }
            },0)
        }
        
        if(self.status === 'rejected'){
            // 同13
            setTimeout(()=>{
                try{
                    let x = onRejected(self.reason) 
                    resolvePromise(promise2,x,resolve,reject)
                }catch(e){
                    reject(e)
                }
            },0)
        }
    
        if(self.status === 'pending'){
            self.onResolvedCallbacks.push(()=>{
                // 同13
                setTimeout(()=>{
                    try{
                        let x =  onFulfilled(self.value)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            }) 
            self.onRejectedCallbacks.push(()=>{
                // 同13
                setTimeout(()=>{
                    try{
                        let x = onRejected(self.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                },0)
            })
        }
    })
    return promise2
}

// 14.到目前为止,根据promiseA+规范的代码写得差不多了,我们可以通过测试代码来测试我们是否写得正确,下面我们写一段测试代码

Promise.defer = Promise.deferred = function(){
    let dfd = {}
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}

// 14.接下来我们要安装一个插件,npm install promises-aplus-test -g

module.exports = Promise
// 完整代码 也顺便带大家理顺一下
function Promise(executor) {
    let self = this;
    self.value = undefined;  // 成功的值
    self.reason = undefined;  // 失败的值
    self.status = 'pending'; // 目前promise的状态pending
    self.onResolvedCallbacks = []; // 可能new Promise的时候会存在异步操作,把成功和失败的回调保存起来
    self.onRejectedCallbacks = [];
    function resolve(value) { // 把状态更改为成功
        if (self.status === 'pending') { // 只有在pending的状态才能转为成功态
            self.value = value;
            self.status = 'resolved';
            self.onResolvedCallbacks.forEach(fn => fn()); // 把new Promise时异步操作,存在的成功回调保存起来
        }
    }
    function reject(reason) {  // 把状态更改为失败
        if (self.status === 'pending') { // 只有在pending的状态才能转为失败态
            self.reason = reason;
            self.status = 'rejected';
            self.onRejectedCallbacks.forEach(fn => fn()); // 把new Promise时异步操作,存在的失败回调保存起来
        }
    }
    try {
        // 在new Promise的时候,立即执行的函数,称为执行器
        executor(resolve, reject);
    } catch (e) { // 如果执行executor抛出错误,则会走失败reject
        reject(e);
    }
}

// 这个函数为核心,所有的promise都遵循这个规范
// 主要是处理then中返回的值x和promise2的关系
function resolvePromise(promise2,x,resolve,reject){
    // 当promise2和then返回的值x为同一个对象时,变成了自己等自己,会陷入死循环
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle'));
    }
    let called;
    // x可能是一个promise也可能是一个普通值
    if(x!==null && (typeof x=== 'object' || typeof x === 'function')){
        try{
            let then = x.then; 
            if(typeof then === 'function'){
                then.call(x,y=>{ 
                    if(called) return; 
                    called = true;
                    resolvePromise(promise2,y,resolve,reject);
                },err=>{ 
                    if(called) return;
                    called = true;
                    reject(err);
                });
            }else{
                resolve(x);
            }
        }catch(e){
            if(called) return;
            called = true;
            reject(e);
        }
    }else{ 
        resolve(x);
    }
}

// then调用的时候,都是属于异步,是一个微任务
// 微任务会比宏任务先执行
// onFulfilled为成功的回调,onRejected为失败的回调
Promise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
    onRejected = typeof onRejected === 'function'?onRejected: err=>{throw err}
    let self = this;
    let promise2;
    // 上面讲了,promise和jquery的区别,promise不能单纯返回自身,
    // 而是每次都是返回一个新的promise,才可以实现链式调用,
    // 因为同一个promise的pending resolve reject只能更改一次
    promise2 = new Promise((resolve, reject) => {
        if (self.status === 'resolved') {
            // 为什么要加setTimeout?
            // 首先是promiseA+规范要求的
            // 其次是大家写的代码,有的是同步,有的是异步
            // 所以为了更加统一,就使用为setTimeout变为异步了,保持一致性
            setTimeout(()=>{
                try { // 上面executor虽然使用try catch捕捉错误
                      // 但是在异步中,不一定能够捕捉,所以在这里
                      // 用try catch捕捉
                    let x = onFulfilled(self.value);
                    // 在then中,返回值可能是一个promise,所以
                    // 需要resolvePromise对返回值进行判断
                    resolvePromise(promise2,x,resolve,reject);
                } catch (e) {
                    reject(e);
                }
            },0)
        }
        if (self.status === 'rejected') {
            setTimeout(()=>{
                try {
                    let x = onRejected(self.reason);
                    resolvePromise(promise2,x,resolve,reject);
                } catch (e) {
                    reject(e);
                }
            },0)
        }
        if (self.status === 'pending') {
            self.onResolvedCallbacks.push(() => {
                setTimeout(()=>{
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                },0)
            });
            self.onRejectedCallbacks.push(() => {
                setTimeout(()=>{
                    try {
                        let x = onRejected(self.reason);
                        resolvePromise(promise2,x,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                },0)
            });
        }
    });
    return promise2
}
Promise.defer = Promise.deferred = function(){
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve = resolve;
        dfd.reject = reject;
    })
    return dfd;
}
module.exports = Promise;
执行promises-aplus-tests promise.js

На данный момент мы написали обещание, соответствующее спецификации promiseA+, вы можете прочитать его еще несколько раз.


Далее давайте улучшим этот промис и напишем часто используемые методы промисов.

  • Promise.reject
  • Promise.resolve
  • catch
  • Promise.all
  • Promise.race
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason);
    })
}

Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{
        resolve(value);
    })
}

Promise.prototype.catch = function(onRejected){
    return this.then(null,onRejected);
};

Promise.all = function(promises){
    return new Promise((resolve,reject)=>{
        let 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(data=>{
                processData(i,data);
            },reject);
        }
    })
}

Promise.race = function(promises){
    return new Promise((resolve,reject)=>{
        for(let i = 0;i<promises.length;i++){
            promises[i].then(resolve,reject);
        }
    })
}

Обещание, как распространенный способ достичь этого?

Promise.resolve / Promise.reject
// 原生的Promise.resolve使用
Promise.resolve('hello swr').then((data)=>{ // 直接把成功的值传递给下一个then
    console.log(data) // hello swr
})

// 那么Promise.resolve内部是怎么实现的呢?
Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{ // 在内部new一个Promise对象
       resolve(value) 
    })
}

// 同理,Promise.reject内部也是类似实现的
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        reject(reason)
    })
}
поймать, как этого добиться?
// 原生Promise的catch使用
Promise.reject('hello swr').catch((e)=>{
    console.log(e) // hello swr
})

// 上面这段代码相当于下面这段代码
Promise.reject('hello swr').then(null,(e)=>{ // then里直接走了失败的回调
    console.log(e) // hello swr
})

// 内部实现
Promise.prototype.catch = function(onRejected){
    return this.then(null,onRejected) // 相当于then里的成功回调只传个null
}


Promise.all, этот метод очень важен, одновременно выполняет несколько асинхронных операций и возвращает новое обещание, успешное значение — это массив, порядок элементов массива — это порядок параметров, переданных в Promise.all
// 原生Promise.all的使用
// 假设1.txt内容为hello 2.txt内容为swr
let fs = require('fs')
function read(filePath,encoding){
    return new Promise((resolve,reject)=>{ 
        fs.readFile(filePath,encoding,(err,data)=>{
            if(err) reject(err)
            resolve(data)
        })
    })
}

Promise.all([read('./1.txt','utf8'),read('./2.txt','utf8')]).then((data)=>{
    console.log(data) // 全部读取成功后返回 ['hello','swr']
                      // 需要注意的是,当其中某个失败的话,则会走失败的回调函数
})

// 内部实现
Promise.all = function(promises){ // promises 是一个数组
    return new Promise((resolve,reject)=>{
        let arr = []
        let i = 0
        function processData(index,data){
            arr[index] = data
            // 5.我们能用arr.length === promises.length来判断请求是否全部完成吗?
            // 答案是不行的,假设arr[2] = 'hello swr'
            // 那么打印这个arr,将是[empty × 2, "hello swr"],
            // 此时数组长度也是为3,而数组arr[0] arr[1]则为空
            // 那么换成以下的办法
            if(++i === promises.length){ // 6.利用i自增来判断是否都成功执行
                resolve(arr) // 此时arr 为['hello','swr']
            }
        }
        
        for(let i = 0;i < promises.length;i++){ // 1.在此处遍历执行
            promises[i].then((data)=>{ // 2.data是成功后返回的结果
                processData(i,data) // 4.因为Promise.all最终返回的是一个数组成员按照顺序排序的数组
                                    // 而且异步执行,返回并不一定按照顺序
                                    // 所以需要传当前的i
            },reject) // 3.如果其中有一个失败的话,则调用reject
        }
    })
}
Promise.race Этот метод предназначен для одновременного выполнения нескольких асинхронных операций, а затем, в зависимости от того, что быстрее, используйте результат какого из них, гонка означает гонку.
// 原生Promise.race的使用
// 一个成功就走成功的回调,一个失败就走失败的回调
Promise.race([read('./1.txt','utf8'),read('./2.txt','utf8')]).then((data)=>{
    console.log(data) // 可能返回 'hello' 也可能返回 'swr' 看哪个返回快就用哪个作为结果
})

// 内部实现
Promise.race = function(promises){ // promises 是一个数组
    return new Promise((resolve,reject)=>{
        for(let i = 0;i < promises.length;i++){ 
            promises[i].then(resolve,reject) // 和上面Promise.all有点类似
        }
    })
}

Как понять синтаксический сахар Promise.defer = Promise.deferred?

Этот синтаксический сахар может упростить некоторые операции, такие как

let fs = require('fs')
// 写法一:
function read(filePath,encoding){
    // 这里的new Promise依然是传递了一个executor回调函数
    // 我们该怎样减少回调函数嵌套呢?
    return new Promise((resolve,reject)=>{ 
        fs.readFile(filePath,encoding,(err,data)=>{
            if(err) reject(err)
            resolve(data)
        })
    })
}

// 写法二:
// 这样的写法减少了一层回调函数的嵌套
function read(filePath,encoding){
    let dfd = Promise.defer()
    fs.readFile(filePath,encoding,(err,data)=>{
        if(err) dfd.reject(err)
        dfd.resolve(data)
    })
    return dfd.promise
}

read('./1.txt','utf8').then((data)=>{
    console.log(data)
})



Вывод: Когда я писал это впервые, я думал о том, где я это написал. Пожалуйста, простите меня~ Я также надеюсь быть полезным для вас. Реализация промисов в исходном коде, мой самый большой выигрыш не в том, как реализовать промисы, а в программировании мышления , каждый может много думать об этом, и я надеюсь, что вы сможете общаться со мной и вместе добиться прогресса