Promise + Promise部分底层源码

60 阅读14分钟

Promise的使用

使用promise的目的:就是为了解决我们的回调地狱的问题,这个也是我们的一种方案来实现基本的异步编程的解决方案
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button>发送ajax请求</button>
​
  <script>
    // 下面的就是一个简单像后端发送请求的步骤
    // 1.首先先获取我们的那个button节点
    let button = document.querySelector("button");
    // 2.设置点击事件
    button.addEventListener("click", function(){
      // 3.创建ajax实例化对象
      let xhr = new XMLHttpRequest();
      // console.log(xhr)
      // 4.open方法,设置method,和服务端的网址,这一步就是请求
      xhr.open("get",'http://172.20.3.242/cqupt/#/login', true)
      // 5.xhr.send(请求体数据),注意我们的get请求是没有请求体的,只有我们的post请求才有请求体
      xhr.send(null);
      // 6.客户端开始接收服务端返回的数据
      xhr.onreadystatechange = function(){
        // 含有0 1 2 3 4的值
        /*
        请求状态码
        0: 请求未初始化
        1: 服务器连接已建立
        2: 请求已接收
        3: 请求处理中(响应中通常部分数据可获得)
        4: 请求已完成,且响应已就绪
        */
        // console.log(xhr.readyState)
        if(xhr.readyState === 4){
          // 响应状态码200-299之间就是成功的响应
          if(xhr.status >= 200 || xhr.status < 300){
            // 注意基本上现在的后端的话,返回给我们是那个json数据,这个时候我们就要实现对这个数据的基本的那个转化
            // json字符串 ==》 json对象:JSON.parse
            // json对象 ==》 json字符串:JSON.stringify
            console.log(JSON.parse(xhr.responseText))
          }
        }
      }
    })
  </script>
</body>
</html>

Promise的状态

promise实现就是一个基本的异步编程的实现
​
一般的话,实现promise的时候,我们实现的我们的那个:let promise_name = new Promise(callback)
​
使用promise就是为了实现基本的一些更好的控制我们的异步的操作
promise 是具有三个状态的:
​
1.pending 进行中
​
2.fulfilled 已成功
​
3.rejected 已失败
// 注意下面的参数名只是为了规范才这么写的
const p1 = new Promise((resolve, reject)=>{
// 最开始的状态是我们的那个pending状态
// 开始调整我们的状态
   resolve("ok") // 实现的是将我们的状态调整为fulfilled状态
   reject("error") // 实现的就是我们的状态调整为rejected状态
​
   // 注意状态一确定,那么状态就不会改变了
});
console.log(p1)
​
/*
注意我们的那个异步的状态的话:
pending ==> fulfilled
或者
pending ==> rejected
*/

Promise 状态确定后的 then的方法

首先我们通过我们传入的参数:resolve 和 reject 来实现确定我们的最后的状态
​
resolve ==> fulfilled状态
reject ==> rejected状态
promise实例化对象后面含有三中方法:then / catch / finally
    /*
    then 是用于得到成功value后的成功回调和用于得到失败的reason的回调,并且将返回一个新的promise实例化对象
​
    成功的时候:那么就执行then方法的第一个回调函数
    失败的时候:那么就执行then方法的第二个回调函数
    */
    // 注意下面的参数名只是为了规范才这么写的
    const p1 = new Promise((resolve, reject)=>{
      // resolve("ok") 
      reject("error") 
    })
    console.log(p1)
    console.dir(Promise)
​
    const result = p1.then(value=>{
      console.log(value)
    }, reason=>{
      console.log(reason)
    })
​
    // 这个时候,我们的result又是一个promise对象
    console.log(result)
    result.then(value => {}, reason => {})

Promise then方法的链式调用

const p1 = new Promise((resolve, reject)=>{
    // 开始设置状态为成功
    resolve("ok fulfilled")
})
​
// 开始实现promise的链式调用
p1.then(value => {
    console.log(value)
    console.log("第一次链式调用")
}).then(value => {
    console.log(value)
    console.log("第二次链式调用")
    // 设置了这个,我们的后面的内容就不执行了
    return new Promise((resolve, reject) => {
      reject("error rejected")
    })
}).then(value => {
    console.log(value)
    console.log("第三次链式调用")
})
​
// 注意我们的下一步的状态的设置和我们的上一步的状态是息息相关的

Promise then方法的链式读取文件

// import {fs} from "fs";
const fs = require("fs")
​
// 开始设置我们的promise
const p1 = new Promise((resolve, reject) => {
  fs.readFile("./01.txt", (err, data) => {
    if(err){
      reject(err)
      console.log(err)
    }else{
      resolve(data)
      console.log(data)
    }
  })
}).then(value=>{
  return new Promise((resolve, reject) =>{
    fs.readFile("./01.txt", (err, data)=>{
      if(err){
        reject([data, err])
      }else{
        resolve([value, data])
      }
    })
  })
}, reason=>{})
// 先实现导入模块
const fs = require("fs");
​
// 定义函数
function ReadFileFunc(path){
    return new Promise((resolve, reject)=>{
        fs.readfile(path, (err, data)=>{
            if(err){
                reject(err)
            }else{
                // 注意我们的这里的读取只是buffer
                resolve(data)
            }
        })
    }).then(value=>{
        console.log(value.toString());
    })
}

Promise.all 方法的使用

// 首先先实现我们的那个创建几个promise对象const p1 = new Promise((resolve, reject)=>{
    resolve("ok")
})
​
const p2 = new Promise((resolve, reject)=>{
    resolve("ok")
})
​
const p3 = new Promise((resolve, reject)=>{
    resolve("ok")
})
​
// 开始使用all方法
let result = Promise.all([p1, p2, p3])
console.log(result)
​
​
// 就是对多个promise实现的判断处理 ,传入的是数据,返回的是promise对象
// 只有我们的所有Promise对象全是成功的,才可以成功
// 开始实现模拟接口请求数据
function getUserList(method, url, content){
    return new Promise((resolve, reject)=>{
        // 书写ajax请求数据
        let xhr = new XMLHttpRequest()
        xhr.open(method, url,true);
        if(content){
            xhr.send(content)
        }else{
            xhr.send(null)
        }
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status >= 200 && xhr.status < 300){
                    try{
                        const result = JSON.parse(xhr.responseText)
                        resolve(result)
                    }catch(error){
                        reject(error)
                    }
                }else{
                    reject(new Error(`network is not ok ${xhr.statusText}` ))
                }
            }
        }
    })
}
// 开始实现模拟几个promise函数
function ReadData(){
    return new Promise((resolve, reject)=>{
        setInterval(function(){
            resolve("成功读取数据")
        }, 1000)
    })
}
​
function ReadText(){
    return new Promise((resolve, reject)=>{
        setInterval(function(){
            resolve("成功读取文章")
        }, 2000)
    })
}
​
function ReadBanner(){
    return new Promise((resolve, reject)=>{
        setInterval(function(){
            resolve("成功读取Banner")
        }, 3000)
    })
}
​
// 开始使用all方法
let result = Promise.all(ReadData(), ReadText(), ReadBanner())

Promise allSettled方法

这个方法就是实现判断我们的最后的那个是否执行完
// 开始实现模拟接口请求数据
function getUserList(method, url, content){
    return new Promise((resolve, reject)=>{
        // 书写ajax请求数据
        let xhr = new XMLHttpRequest()
        xhr.open(method, url,true);
        if(content){
            xhr.send(content)
        }else{
            xhr.send(null)
        }
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status >= 200 && xhr.status < 300){
                    try{
                        const result = JSON.parse(xhr.responseText)
                        resolve(result)
                    }catch(error){
                        reject(error)
                    }
                }else{
                    reject(new Error(`network is not ok ${xhr.statusText}` ))
                }
            }
        }
    })
}
​
// 开始实现我们的那个allSettled方法的使用
Promise.allSeettled([getUserList("get", "sdbcidc"), getUserList("get", "cvdscfsd")]).then(value=>{
    let successList = value.filter(item => item.status === "fulfilled");
    console.log(successList)
}).catch(error=>{
    console.log(error)
})

Promise.any 方法

只要有一个promise是成功的,那么最后的就是成功的
只要传入的参数Promise的状态一个是fulfilled ,那么最后的状态就是fulfilled
const p1 = new Promise((resolve, reject)=>{
    resolve("ok")
})
​
const p2 = new Promise((resolve, reject)=>{
    resolve("ok")
})
​
const p3 = new Promise((resolve, reject)=>{
    reject("error")
})
​
let result = Promise.any([p1, p2, p3])
console.log(result)

Promise.race() 方法

就是谁先获得状态,那么就先实现使用那个的状态
const p1 = new Promise((resolve, reject)=>{
    resolve("1")
})
​
const p2 = new Promise((resolve, reject)=>{
    resolve("2")
})
​
const p3 = new Promise((resolve, reject)=>{
    reject("3")
})
​
let result = Promise.any([p1, p2, p3])
console.log(result)

Promise.reject() 和 Promise.resolve()

Promise.reject()始种实现的是我们的那个拒绝的状态
​
​
resolve:如果里面的参数是一个那个非promise对象,那么就是我们的fulfilled
        如果是我们的那个promise对象,那么最后的状态就是取决于后面的那个promise状态

Promise.catch方法

catch是主要用来指定我们的失败的时候的回调函数,返回的是一个promise实例化对象
const p1 = new Promise((resolve, reject)=>{
    reject("error")
})
​
p1.then(value=>{
    console.log(value)
}).catch(error=>{
    console.log(error)
})

Promise.finally方法

就是实现的基本的我们的不管最后的状态如何,我们都实现运行下面的结果

Promise终止链条

就是子啊我们的Promise语句中使用我们的最后的那个: return new Promise(()=>{})
终止promise链条的实质上就是就是返回一个pending状态的Promise对象
promise的状态只有三种: pending / fulfilled / rejected
​
​
实现修改我们的promise的状态的方法:
1.通过resolve来实现
2.通过reject来实现
3.通过throw来实现
​
​
使用promise的时候,我们先确定状态还是先确定回调函数
1.如果说是实现的同步状态,那么线确定状态,在确定回调
2.如果说是一个异步实现,那么就是先确定回调,在确定状态
这个的话就是通过的我们的判断状态后实现的回调函数,先确定状态在执行

async函数

// // 以前的代码
const p1 = new Promise((resolve, reject)=>{
    // 后面的就是实现确定状态
})
​
// 现在的写法
// 就是使用我们的async和awit方法来实现基本的使用同步的写法,来实现异步的效果
// 任何的函数都是可以实现我们的定义为异步的函数
// 就是使用我们的最后的那个在函数的前面实现添加一个async的关键字即可
async function func(){
    // console.log("啊哈哈啊哈")
    
    // 实现的是返回的是我们的一个非promise的数据
    // return 1000;
    
    // 实现返回的是我们的promise对象
    return new Promise((resolve, reject) => {
        
    })
    // 注意我们的不管是什么数据,最后得到的还是一个promise对象
    
    // 抛出异常
    // thrpw new Error("错误")
}
func();
​
func().then(value=>{}, reason=>{}).then.....

await表达式

//async结合我们的awit来使用
/*
函数含有那个awit的时候,我们是一定含有那个async的
await 后面如果是一个promise实例化对象,那么就是返回的是一个那个成功状态的promise对象
await 后面如果是一个那个非promise的值,那么实现的返回就是我们的最后的那个数据
*/
async function func(){
    // 1.await右方是一个非promise的数据
    // let re = await 100;
    
    // await的右是一个promise数据
    let re = await new Promise(() => {})
}
​
​
// 首先我们的那个async里面实现的那个是一个基本的一些:里面含有同步和异步的代码
// 那么我们的后面实现的就是最后的那个先执行完同步的代码,然后再执行我们的异步的代码

宏任务和微任务

如果说我们的含有那个同步任务和微任务和宏任务
那么就是先执行我们的同步任务,然后执行我们的微任务,最后执行的才是我们的宏任务
同 微 宏任务
​
宏任务:dom事件回调、ajax事件回调、定时器回调
微任务:promise回调 、 mutation回调
​
然后我们的那个任务的话: heap堆区/ stack栈区==》 就是我们的浏览器的主线程的任务
                     宏任务和微任务就是我们的浏览器执行的分线程的任务
// JS中是用来实现存储的回调函数的两个任务:微任务和宏任务
// 宏任务(宏队列): 用来实现保存的是我们的那个宏任务(回调):定时器/DOM事件回调/ajax
// 微任务(微队列): 用来保存的是我们的微任务(回调): promise / mutation
// JS引擎的话首先先实现的是执行我们的同步任务,等到同步任务执行完了之后,才实现执行我们的微任务,最后执行的才是我们宏任务
// 宏任务和微任务都是在我们的那个宏队列和微队列中实现保存的
// 开始通过我们的那个代码来实现展示我们的执行是顺序/*
同步任务: 2222 4444
微任务: 3333
宏任务: 1111
*/// 宏任务的定义
setTimeout(()=>{
    console.log(1111)
},0)
​
// 微任务的定义
new Promise((resolve, reject)=>{
    resolve()
    console.log(2222)
}).then(value=>{
    console.log(3333)
})
console.log(4444)

Promise 源码的手写

// html文件中的script中的方法
/*
1.首先我们的promise是一个那个构造函数,作用是为了实现基本的创键实例化对象
语法:let 变量名 = new 构造函数名(); 在堆内存中实现开辟一个空间,分配其大小以及设置其地址值
​
在我们的堆内存中主要实现的存储我们的引用数据类型
在我们的栈内存中主要实现存储的是我们的基本数据类型和地址值
​
2.promise是具有一个那个参数的,分为我们的实际参数,其中的参数是一个回调函数(执行器函数 executor)
​
3.执行器函数executor里面也是需要实现接收两个参数的: resolve , reject
​
4.任何一个构造函数上都是含有默认的属性: prototype
  同时我们的Promise的实例化对象是含有两个属性的: PromiseState(实现记录的是我们的最后的一个Promise的状态值)
  PromiseResult(实现记录的是我们的Promise的结果值)
  
5.通过我们的调用resolve方法可以实现我们Promise的状态实现pending -> fulfilled
  通过基本的那个reject + throw的方法,可以实现将promise的状态: pending -> rejected
  
6.executor执行器函数是含有两个形式参数的,一个是resolve和reject
​
7.resolve和reject又是回调函数
        方法一:
        let self = this
        实现修改函数中的this指向方法:call apply bind
        函数名.call(新的this指向,实际参数1, 实际参数2...)
        函数名.apply(新的this指向,实际参数1, 实际参数2...)
        call/apply函数只要一修改完我们的this的指向后,就实现立即执行
        bind的话,不会实现立即执行,而且函数是具有那个返回值的
        返回值是和我们的原本的数据结构是一模一样的的修改完函数的this指向的新函数
        
8.注意我们的Promise的状态的话,只可以实现修改一次,一旦确定了就不可以实现改变了
  这个时候就需要我们实现一个判断
  
9.原型链的实现:我们需要注意的就是我们使用的时候,那个对象的prototype方法和实例化对象的__proto__指向的是相同的
  都是指向的一个空对象,我们是可以通过这一点来实现我们的那个创建我们的使用的方法的,这样可以实现节省我们的内存空间
  然后我们使用的 Object.assign() 就是我们的对象间的合并的方法,将源对象的属性复制到目标对象中,返回目标对象
  Object.assign(target, ...sources)
  target:目标对象
  sources:源对象
  返回值是target
  我们使用这个方法是为了实现最基本的那个在Promise的原型上实现添加then等等的方法
  
10.注意我们的promise对象的关键是我们的异步执行和链式调用
   实现异步操作的话,我们就要实现异步操作的话,就要使用那个定时器
   链式调用的思路:就是实现我们的那个:后面的链式调用的话,是取决于我们的那个前者到底是失败还是成功
   这个时候我们就要使用新的那个promise对象
   在我们的then方法中还是具有三种情况的:就是数据是一个那个promise对象和不是promise对象,以及是否是我们的那个抛出异常
*/
​
​
// 1.创建一个promise的实例化对象
let p1 = new Promise((resolve, reject)=>{
    // console.log("我是一个promise执行器函数")
    /*
    首先我们的原生中的含有的改变状态的方法是我们的那个:
    resolve("成功时候的描述信息")
    reject("失败的时候的描述信息")
    throw "异常信息"
    */
})
console.log(p1)
p1.then(value=>{}, reason=>{})
function Promise() {
    
}
​
// 实现暴露我们的那个Promise
window.Promise = Promise;  // 但是我们再全局上实现添加函数的话会导致后面的那个全局变量的污染,所以说我们使用立即执行函数IIFE
// 立即执行函数
// 可以避免对外部的变量造成污染
(function(window){
    // 只要是给我们的构造函数的身上添加属性,那么,就是在this的身上实现添加属性
    function Promise(executor){
        // console.log("实例化对象", this);
        // 开始实现为我们的promise的身上实现添加实例化的属性:PromiseState和PromiseResult两个属性
        this.PromiseState = "pending"
        this.PromiseResult = undefined
        
        // 开始实现对下面的东西实现封装
        
        // 开始实现改为箭头函数
        // 箭头函数在定义的时候,那么参数只有一个的时候,括号是可以省略的
        // 箭头函数的this的指向就是当前函数的声明的指向(外层函数的this的指向)
        // 定义resolve函数
        
        // if(this.PromiseState !== "pending") return; 使用下面的代码来实现我们的确定后状态后,直接不在修改的功能
        const _resolve = value => {
            if(this.PromiseState !== "pending") return;
            this.PromiseState = "fulfilled"
            this.PromiseResult = value
        }
        // 定义reject函数
        const _reject = value => {
            if(this.PromiseState !== "pending") return;
            this.PromiseState = "rejected"
            this.PromiseResult = value
        }
        
        /*
        // 定义resolve函数
        const _resolve = function(value){
            this.PromiseState = "fulfilled"
            this.PromiseResult = value
        }.bind(this)
        // 定义reject函数
        const _reject = function(value){
            this.PromiseState = "rejected"
            this.PromiseResult = value
        }.bind(this)
        */
        
        // 函数中的this指向的是那个使用他的对象
        try{
            executor(_resolve, _reject);
        }catch(error){
            if(this.PromiseState !== "pending") return;
            this.PromiseState = "rejected"
            this.PromiseResult = error || error.message
        }
    }
    
    // 开始使用我们的Object.assign() 方法和 prototype的方法来实现基本的合并工作来实现基本的方法
    Object.assign(Promise.prototype, {
            // ES6中的函数的简写: 函数名(){}
            // onfulfilled成功的时候的回调函数
            // onrejected 失败的时候的回调函数
            then(onfulfilled, onrejected){
                // 如果调用我们的then方法,会实现返回我们的新的promise实例化对象
                return new Promise((resolve, reject) => {
                    // 为了实现异步调用,这个时候,我们就要使用那个定时器
                    if(this.PromiseState === "fulfilled") {
                        setTimeout(() => {
                            try{
                                const value = onfulfilled(this.PromiseResult)
                                // 判断我们的value是不是promise的实例
                                if(value instanceof Promise){
                                    //value.then(v =>{
                                        //resolve(v)
                                    //}, r =>{
                                        //reject(r)
                                    //})
                                    value.then(resolve, reject)
                                }else{
                                    reject(value)
                                }
                            }catch(e){
                                // 如果有异常,那么我们的状态就是rejected
                                reject(err)
                            }
                        })
                    }else if(this.PromiseState === "rejected"){
                        setTimeout(() => {
                            try{
                                const value = onrejected(this.PromiseResult)
                                // 判断我们的value是不是promise的实例
                                if(value instanceof Promise){
                                    //value.then(v =>{
                                        //resolve(v)
                                    //}, r =>{
                                        //reject(r)
                                    //})
                                    value.then(resolve, reject)
                                }else{
                                    resolve(value)
                                }
                            }catch(err){
                                reject(err)
                            }
                        })
                    }
                })
            },
    })
    Window.Promise = Promise;
})(Window);
​
// 后面的方法就不搓了后面补上
// 需要的联系我,我后面另发