6000字入门Promise

105 阅读17分钟

总所周知,JavaScript 是一门单线程执行的编程语言。也就是说,同一时间 JavaScript 只能做同一件事情。而一旦前一个任务非常耗时,那么后续的任务就不得不一致等待,从而造成程序的假死问题。

本章也就是从这个问题开始讨论,逐步认识 Promise

本篇的所有请求都是利用 json-server 这个包去模拟服务器

同步和异步

什么是同步

简而言之就是:代码自上而下逐行运行,完成了上一句才能下一句。

Snipaste_2022-09-02_10-59-26.png

哪怕中间来个循环,也得等,这也就是同步的缺点

Snipaste_2022-09-02_11-01-11.png

什么是异步

通俗的来说,就是 主线程接着走,延迟和等待的程序挂载到另一个列表执行,不阻塞程序。

Snipaste_2022-09-02_11-14-17.png

执行过程

1.同步任务由javascript主线程次序执行
2.异步任务委托宿主环境执行
3.已完成的异步任务对吸引的回调函数,会被加入到任务队列中等待执行
4.javascript主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
5.javascript主线程不断重复上面的第四步

20200714184207672.png

异步的一个实例

function loadImag(src, resolve, reject) {
    let image = new Image();
    image.src = src;
    image.onload = () => {
        resolve(image);
    } // 成功的回调函数
    image.onerror = reject; // 失败的回调函数
}

loadImag(
    "./logo.png",
    image => {
        document.body.appendChild(image);
        console.log("图片加载成功")
    },
    () => {
        console.log("图片加载失败")
    }
);
console.log("啦啦啦");

Snipaste_2022-09-02_14-49-19.png

异步的缺点

Snipaste_2022-09-02_11-21-35.png

上面就是一个典型的异步问题,在代码中同样也会出现这样的问题。

//hd.js
function hd(){
    console.log("hd.js");
}
//hc.js
function hc(){
    hd();
    console.log("hc.js");
}
// index.js
function load(src){
    let script = document.createElement("script");
    script.src = src;
    document.body.appendChild(script);
}
load("./hd.js",()->{
    hd();
});
load("./hc.js",()->{
    hc();
});

不断刷新,时不时会报错

原因 :两个文件,谁先加载完,谁就先放在任务队列里面,因为hc.js依赖于hd.js,所以当hc.js先加载完了之后,就会报错,但是hd.js先加载完不会报错

回调地狱

上面暴露的问题,其实解决起来非常的简单。

function load(src){
    let script = document.createElement("script");
    script.src = src;
    document.body.appendChild(script);
}
load("./hd.js",()->{
    load("./hc.js",()->{
    	hc();
    });
});

在这个时候,浏览器会先加载 hd ,再加载 hc ,放进任务队列的顺序就不会变,所以无论怎么刷新,都不会报错。

但是,当我们的模块依赖过多,且一层套着一层的时候,就会出现所谓的 回调地狱 问题。

Snipaste_2022-09-02_15-00-11.png

如上面, 代码冗长,且不便于异常处理。

初识Promise

为什么要使用 Promise

这个问题其实在上文已经说到,回调地狱代码冗长且不易于处理

Promise的优点就格外的明显了:

  1. Promise 支持链式编程
  2. Promise 可以随时监听异步任务的状态,随时指定回调函数,一个或者多个

Promise实例

Promise 实现 ajax

首先我们不用 Promise 实现 ajax。

const btn = document.querySelector('#btn')

// 不用 Promise
btn.addEventListener('click',function(){
    // 创建对象
    const xhr = new XMLHttpRequest();
    // 初始化
    xhr.open('GET','http://127.0.0.1:3000/posts/1')
    // 发送
    xhr.send();
    // 处理响应结果
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
            if(xhr.status >= 200 && xhr.status < 300){
                console.log(xhr.response)
            }else{
                console.log(xhr.status)
            }
        }
    }
})

接下来,我们初步体验一下 Promise

const btn = document.querySelector('#btn')

// 用 Promise
btn.addEventListener('click', function () {
    const p = new Promise((resolve, reject) => {
        // 创建对象
        const xhr = new XMLHttpRequest();
        // 初始化
        xhr.open('GET', 'http://127.0.0.1:3000/posts/1')
        // 发送
        xhr.send();
        // 处理响应结果
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.response)
                } else {
                    reject(xhr.status)
                }
            }
        }
    })

    // 调用 then 方法
    p.then(value => {
            console.log(value)
        },
        reason => {
            console.log(reason)
        }
    )
})

可以看到,Promise 有两种状态,成功失败 ,并且把这两个状态分别抽象成了一个回调函数。

Promise 封装 ajax

function sendAJAX(method, url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(method, url)
        xhr.send()
        // 处理结果
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.response)
                } else {
                    reject(xhr.status)
                }
            }
        }
    })
}

sendAJAX('GET', 'http://127.0.0.1:3000/posts/1').then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})

Promise 对象中的属性

Snipaste_2022-09-02_18-52-37.png

Promise对象的状态

Promise 实例对象中有一个属性 PromiseState,这个属性有三个状态:

pending 等待 / 未决定的状态
resolved / fullfilled 成功
rejected 失败

Promise 状态改变:

Promise 的状态改变,有且只有两种:

pending 变为 resolved
pending 变为 rejected

每一个 Promise 对象只能改变一次。无论是成功还是失败,都会有一个结果数据,成功的结果数据一般称为 value ,失败的结果数据一般称为 reason 。

Promise对象的值

Promise 对象中的另一个属性 PromiseResult 保存这对象 【成功/失败】 的结果。

Promise 的工作流程

Snipaste_2022-09-02_19-06-02.png

Promise 的 API

Promise 中的 then 方法

一个 Promise 函数可以调用多个 成功/失败 的函数。并且,then如果没有处理发送出来的东西,将一直向下找,直到找到

let p = new Promise((resolve,reject)=>{
        resolve('ok')
})

p.then()

p.then(value => {
    console.log(value)
})

p.then(value => {
    alert(value)
})

Snipaste_2022-09-02_21-11-59.png

上面的例子其实也可以看出 then 方法返回的其实也是一个 Promise 对象,下面这个例子看的更加的清楚。

let p1 = new Promise((resolve, reject) => {
    reject("rejected");
});

let p2 = p1
    .then(
        value => console.log(value),
        reason => console.log(reason)
    )
    .then(
        a => console.log("成功"),
        b => console.log(b)
    );
// 输出 rejected 成功

接下来。我们讨论 then 方法的返回结果。

let p3 = new Promise((resolve,reject) => {
    resolve('ok')
})
let result = p.then(value => {
    // 1.抛出错误
    throw '出了问题';
})

console.log(result)

Snipaste_2022-09-03_09-45-13.png

如果抛出了问题,那么 then 方法就会返回一个 rejected 的结果,返回的值就是抛出错误的值。

let p3 = new Promise((resolve,reject) => {
    resolve('ok')
})
let result = p3.then(value => {
    // 2.返回结果是 非Promise 对象
    return 521;
})

console.log(result)

Snipaste_2022-09-03_09-48-42.png

如果返回的是一个 非Promise 对象,比如数字,字符串等等,那么 then 方法返回的是一个 resolve 的结果,返回的值就是 return 的东西。

let p3 = new Promise((resolve,reject) => {
    resolve('ok')
})
let result = p3.then(value => {
    // 3.返回的是一个 Promise 对象
    return new Promise((resolve,reject)=>{
        reject('Error')
    },reason =>{
        console.warn(reason)
    })
})

console.log(result)

Snipaste_2022-09-03_09-53-21.png

如果返回的是一个 Promise 对象,那么 then 方法返回的结果和值都由 返回的Promise 决定。

Promise.resolve 方法

这里的 resolve 方法是指 Promise 对象(而非实例)上的方法。

let p1 = Promise.resolve(521);
console.log(p1);

Snipaste_2022-09-02_19-37-40.png

如果传入的参数为 非Promise类型的对象(字符串,数字,undefined等等),则返回的结果过为成功 Promise 对象。

let p2 = Promise.resolve(new Promise((resolve,reject) => {
    resolve('ok')
}))
console.log(p2)

Snipaste_2022-09-02_19-42-59.png

let p3 = Promise.resolve(new Promise((resolve,reject) => {
    resolve('Error')
}))
console.log(p3)

Snipaste_2022-09-02_19-49-48.png

如果传入的参数为 Promise 对象,则参数的结果决定了 resolve 的结果。

Promise.reject 方法

Promise.reject 永远返回的都是失败的对象,且失败对象的值就是传入的值。

let p1 = Promise.reject(521);
console.log(p1)

let p2 = Promise.reject(new Promise((resolve,reject) => {
    resolve('ok')
}))
console.log(p2)

let p3 = Promise.reject(new Promise((resolve,reject) => {
    reject('Error')
}))
console.log(p3)

Snipaste_2022-09-02_19-51-41.png

Promise.all 方法

Promise.all 方法,数组内全部都成功,最终的结果成功;数组内有一个不成功,最终结果都是不成功,且值为不成功的那个Promise实例的值。

let p1 = new Promise((resolve,reject)=>{
    resolve('ok')
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.all([p1,p2,p3]);
console.log(result)

Snipaste_2022-09-02_19-59-40.png

let p1 = new Promise((resolve,reject)=>{
    resolve('ok')
})
let p2 = Promise.reject('Error')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.all([p1,p2,p3]);
console.log(result)

Snipaste_2022-09-02_20-01-36.png

Promise.race 方法

Promise.race 方法,返回一个新的 Promise ,第一个完成的 Promise 的结果状态就是最终的结果状态。

let p1 = new Promise((resolve,reject)=>{
    resolve('ok')
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.race([p1,p2,p3]);
console.log(result)

Snipaste_2022-09-02_20-16-27.png

这里返回的就是第一个 Promise 对象的结果。

let p1 = new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('ok')
    },1000)
})
let p2 = Promise.resolve('Success')
let p3 = Promise.resolve('oh Yeah!')
const result = Promise.race([p1,p2,p3]);
console.log(result)

Snipaste_2022-09-02_20-18-25.png

因为前面有一个定时器,所以此时返回的是第二个Promise的结果。

Promise 的链式编程

链式编程的一个实例

let p = new Promise((resolve,reject) => {
    setTimeout(()=>{
        resolve('ok');
    },1000)
})

p.then(value => {
    console.log(value)
    return new Promise((resolve,reject) => {
        resolve('success')
    })
}).then(value => {
    console.log(value)
}).then(value => {
    console.log(value)
})

Snipaste_2022-09-03_10-08-28.png

then 方法返回的是一个 Promise ,而 Promise 的状态是由 then 方法的返回值来决定的,由于 倒数第二个then方法 没有返回值,所以 该then方法 返回的其实是一个 状态为resolve 值为 undefined 的 Promise。这也就是为什么最后打印的是 undefined了。

异常穿透

在链式编程的时候,我们不需要反复的去写 reject 的回调函数,只要在最后加上 catch 这个函数,在链式编程的任何位置发生错误,都能被捕获到。

let p = new Promise((resolve,reject) => {
    setTimeout(()=>{
        reject('Error');
    },1000)
})
p.then(value => {
    console.log(111)
}).then(value => {
    console.log(222)
}).then(value => {
    console.log(333)
}).catch(reason => {
    console.warn(reason)
})

Snipaste_2022-09-03_10-26-27.png

中断Peomise链

想要中断 Promise 链,其实很简单,只需要在想要中断的位置,返回一个 pending 状态的 Peomise 。

let p = new Promise((resolve,reject) => {
    setTimeout(()=>{
        reject('Error');
    },1000)
})
p.then(value => {
    console.log(111)
    // 中断 Promise 链
    return new Promise(()=>{})
}).then(value => {
    console.log(222)
}).then(value => {
    console.log(333)
}).catch(reason => {
    console.warn(reason)
})

finally

finally 这个函数放在最后面,表示前面的东西无论成功与否,函数内的代码都会执行。

new Promise((resolve, reject) => {
        reject("rejected");
    }).then(
        value => {
            return new Promise((resolve, reject) => {
                reject("p2");
            });
        })
    .then(value => {
        console.log(value);
    }).catch(error => {
        console.log(error)
    })
    .finally(() => {
        console.log("永远执行");
    })

Snipaste_2022-09-03_10-40-14.png

自定义封装 Promise

环境搭建

Snipaste_2022-09-04_09-43-58.png

function Promise(executor){
    // resolve 函数
    function resolve(data){

    }
    // reject 函数
    function reject(data){

    }
    // 同步调用 【执行器函数】(Promise 内部的代码是同步调用的,所以必须调用这个函数)
    executor(resolve,reject)
}

// 添加 then 方法
Promise.prototype.then = function(onResolve,onReject){

}

这样一个初步的 Promise 环境就搭好了。

resolve 和 reject 函数的实现

Snipaste_2022-09-02_20-01-36.png

首先,我们在之前打印的 Promise 对象中,我们能发现,Promise 对象有两个属性,也就是上文经常提到的 状态和属性

所以,在实现 resolve和reject 函数的时候,首先必须定义这两个属性,并且在对应的函数中修改 状态和值 这两个属性。

function Promise(executor){
    // 添加属性
    this.PromiseState = 'pending'
    this.PromiseResult = null

    const _this = this

    // resolve 函数
    function resolve(data){
        // 1.修改对象的状态(promiseState)
        _this.PromiseState = 'fulfilled'
        // 2.设置对象结果值(promiseResult)
        _this.PromiseResult = data
    }
    // reject 函数
    function reject(data){
        _this.PromiseState = 'rejected'
        _this.PromiseResult = data
    }
    // 同步调用 【执行器函数】
    executor(resolve,reject)
}

注意:上面的 const _this = this 是因为原本下面的 this 指向全局,也就是 window ,所以我们必须在前面改变this的指向。

throw 抛出异常改变状态

如果不对异常进行处理的话,那么程序一旦 throw 了一个错误出来,程序直接停止执行,根本不会执行后面的代码。

通过之前的学习,我们知道,Promise throw出一个错误后,Promise 的状态变为失败,值为抛出的异常。

try{
        // 同步调用 【执行器函数】
        executor(resolve,reject)
    }catch(e){
        // 调用 reject 将状态变为 reject
        reject(e)
    }

将 Promise 的状态变为只能修改一次

Snipaste_2022-09-04_10-31-22.png

上面这个例子,在我们之前学习的过程中,Promise 应该根据前一个(成功)状态进行改变,但是,我们封装的 Promise 会执行两次,最终下面的 reject 会覆盖掉上面的 resoleve。

Snipaste_2022-09-04_10-37-59.png

解决的方法很简单,在resoleve 和 reject函数中加一个判断即可。

function reject(data){
    // 判断状态
    if(_this.PromiseState !== 'pending') return
    _this.PromiseState = 'rejected'
    _this.PromiseResult = data
}

封装 then 方法执行回调

Promise.prototype.then = function(onResolve,onReject){
    // 调用回调函数
    if(this.PromiseState === 'fulfilled'){
        onResolve(this.PromiseResult)
    }
    if(this.PromiseState === 'rejected'){
        onReject(this.PromiseResult)
    }
}

异步回调的执行

Snipaste_2022-09-04_10-53-44.png

当我们正常执行上面这个例子的时候,我们会发现,在1s后会输出ok。

但是用我们自己封装的 Promise 则不会,原因是我们自己封装后,上面的例子是一个同步代码,resolve 函数在 1s 后执行,在此之前,then 方法就被执行了,所以没有输出结果。

解决办法:

function Promise(executor){
    // 添加属性
    this.PromiseState = 'pending'
    this.PromiseResult = null

    this.callback = {}

    const _this = this

    // resolve 函数
    function resolve(data){
        if(_this.PromiseState !== 'pending') return
        _this.PromiseState = 'fulfilled'
        _this.PromiseResult = data
        // 调用成功的回调
        if(_this.callback.onResolve){
            _this.callback.onResolve(data);
        }
    }
    // reject 函数
    function reject(data){
        if(_this.PromiseState !== 'pending') return
        _this.PromiseState = 'rejected'
        _this.PromiseResult = data
        // 调用失败的回调
        if(_this.callback.onReject){
            _this.callback.onReject(data);
        }
    }
}

// 添加 then 方法
Promise.prototype.then = function(onResolve,onReject){
    if(this.PromiseState === 'fulfilled'){
        onResolve(this.PromiseResult)
    }
    if(this.PromiseState === 'rejected'){
        onReject(this.PromiseResult)
    }
    // 判断 pending 状态
    if(this.PromiseState === 'pending'){
        // 保存回调函数
        this.callback = {
            onResolve,
            onReject
        }
    }
}

解释一下吧,先执行下面的 then 方法,此时 状态还是 pending ,所以将函数保存在 callback 里面。然后执行异步里面的代码(这里是setTimeout里面的),执行 resolve 函数,调用成功的回调。

指定多个回调的实现

Snipaste_2022-09-04_12-41-17.png

上面这个案例,根据之前所学习的知识,我们能得到,上面这个案例会在控制台和弹窗都打印 ok。

但是,我们封装的 Promise 只会弹出弹框。原因是:我们上面保存的回调函数被执行了两遍,前面的一次保存(控制台输出)被后面的一次保存(弹框输出)给覆盖了。因此只有一个输出。

解决办法:

function Promise(executor){
    // 添加属性
    this.PromiseState = 'pending'
    this.PromiseResult = null

    this.callback = []

    const _this = this

    // resolve 函数
    function resolve(data){
        if(_this.PromiseState !== 'pending') return
        _this.PromiseState = 'fulfilled'
        _this.PromiseResult = data
        // 调用成功的回调
        _this.callback.forEach(item => {
          item.onResolve(data)  
        })
    }
    // reject 函数
    function reject(data){
        if(_this.PromiseState !== 'pending') return
        _this.PromiseState = 'rejected'
        _this.PromiseResult = data
        // 调用失败的回调
        _this.callback.forEach(item => {
            item.onReject(data)  
          })
    }
}

// 添加 then 方法
Promise.prototype.then = function(onResolve,onReject){
    if(this.PromiseState === 'fulfilled'){
        onResolve(this.PromiseResult)
    }
    if(this.PromiseState === 'rejected'){
        onReject(this.PromiseResult)
    }
    // 判断 pending 状态
    if(this.PromiseState === 'pending'){
        // 保存回调函数
        this.callback.push({
            onResolve,
            onReject
        }) 
    }
}

Snipaste_2022-09-04_12-51-06.png

同步修改状态 then 方法返回结果

Snipaste_2022-09-04_15-59-54.png

上面这个例子,按之前所学,打印的应该是一个 状态为成功,值为undefined 的Promise。(Promise返回结果的确定)

但是,我们封装的直接打印 undefined。原因是:我们的 then 方法中没有返回任何值。

Promise.prototype.then = function (onResolve, onReject) {
    return new Promise((resolve, reject) => {
        // 调用回调函数
        if (this.PromiseState === 'fulfilled') {
            try {
                // 获取回调的执行结果
                let result = onResolve(this.PromiseResult)
                if (result instanceof Promise) {
                    // 如果返回的是 Promise
                    result.then(v => {
                        resolve(v)
                    }, r => {
                        reject(r)
                    })
                } else {
                    // 如果返回的是 非Promise 数据
                    resolve(result)
                }
            }catch(e){
                reject(e)
            }
        }
        if (this.PromiseState === 'rejected') {
            onReject(this.PromiseResult)
        }
        // 判断 pending 状态
        if (this.PromiseState === 'pending') {
            // 保存回调函数
            this.callback.push({
                onResolve,
                onReject
            })
        }
    })
}

异步修改状态 then 方法的返回结果

Snipaste_2022-09-04_16-10-22.png

Snipaste_2022-09-04_16-13-00.png

这里一直是 pending 的原因是:

因为有一个异步,状态没改变,走了下面这段代码,返回了一个 pending 的Promise。

 if (this.PromiseState === 'pending') {
    // 保存回调函数
    this.callback.push({
        onResolve,
        onReject
    })
}

解决方法:

if (this.PromiseState === "pending") {
      // 保存回调函数
      this.callback.push({
        onResolved: function () {
          try {
            // 执行成功的回调函数
            let result = onResolved(_this.PromiseResult);
            // 判断
            if (result instanceof Promise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch(e) {
            reject(e)
          }
        },
        onRejected: function () {
             try {
            // 执行成功的回调函数
            let result = onRejected(_this.PromiseResult);
            // 判断
            if (result instanceof Promise) {
              result.then(
                (v) => {
                  resolve(v);
                },
                (r) => {
                  reject(r);
                }
              );
            } else {
              resolve(result);
            }
          } catch (e) {
            reject(e);
          }
        },
      });
    }

解释一下,这里的原理和上面异步实例的原理差不多,先执行 then ,将 callback 保存,之后调用异步里面的那个 resolve (这里是)函数,走成功的回调,获取一个 Promise 的值,在根据状态和是否是Promise,并调用相应的函数。

封装和完善 then 方法

Promise.prototype.then = function (onResolved, onRejected) {
  const _this = this;
  return new Promise((resolve, reject) => {
    // 封装函数
    function callback(type) {
      try {
        // 获取回调的执行结果
        let result = type(_this.PromiseResult);
        if (result instanceof Promise) {
          // 如果返回的是 Promise
          result.then(
            (v) => {
              resolve(v);
            },
            (r) => {
              reject(r);
            }
          );
        } else {
          // 如果返回的是 非Promise 数据
          resolve(result);
        }
      } catch (e) {
        reject(e);
      }
    }

    if (this.PromiseState === "fulfilled") {
      callback(onResolved);
    }
    if (this.PromiseState === "rejected") {
      callback(onRejected);
    }
    if (this.PromiseState === "pending") {
      this.callback.push({
        onResolved: function () {
          callback(onResolved);
        },
        onRejected: function () {
          callback(onRejected)
        },
      });
    }
  });
};

这里就是将相似的代码抽象成一个函数。

此外,我们在正常的情况下执行下面的代码:

Snipaste_2022-09-04_19-46-42.png

Snipaste_2022-09-04_19-47-16.png

但是我们自己封装的 Promise 函数,却打印的是:

Snipaste_2022-09-04_19-48-05.png

解决方法:

在 resolve 和 reject 函数中加上一个定时器。

setTimeout(() => {
  _this.callback.forEach((item) => {
    item.onResolved(data);
  });
});

在 then 方法的回调函数也加上一个定时器。

if (this.PromiseState === "fulfilled") {
  setTimeout(() => {
    callback(onResolved);
  });
}
if (this.PromiseState === "rejected") {
  setTimeout(() => {
    callback(onRejected);
  });
}

catch 方法

Snipaste_2022-09-04_17-30-17.png

Snipaste_2022-09-04_17-30-29.png

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

异常穿透

Snipaste_2022-09-04_17-48-33.png

Snipaste_2022-09-04_17-48-41.png

错误的原因是,因为有异步,我们先执行 then 方法,保存 callback 中的两个函数,但是,我们传递的只有一个 onResolved 函数,onRejected 函数就为 undefined,因此报错。

Promise.prototype.then = function (onResolved, onRejected) {
  const _this = this;

  if(typeof onRejected !== 'function'){
    onRejected = reason => {
        throw reason
    }
  }
  if(typeof onResolved !== 'function'){
    onResolved = value => {
        return value
    }
  }

  return new Promise((resolve, reject) => {
    ...其他代码
  });
};

如果没有传,那么我们手动的添加一个这样的函数。这样,就完成了异常穿透功能。

resolve 方法

const p1 = Promise.resolve('ok')
console.log(p1)

实现:

Promise.resolve = function(value){
    // 返回Promise对象
    return new Promise((resolve,reject) => {
        if(value instanceof Promise){
            value.then(v => {
                resolve(v)
            },r => {
                reject(r)
            })
        }else{
            resolve(value)
        }
    })
}

reject 方法

const p2 = Promise.reject('Error')
console.log(p2)

实现:

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

all 方法

const p3 = Promise.resolve('111')
const p4 = Promise.reject('222')
const p5 = Promise.resolve('333')

const res = Promise.all([p3,p4,p5])
console.log(res)

实现

Promise.all = function(promises){
    return new Promise((resolve,reject) => {
        let count = 0
        let arr = []
        for(let i = 0;i<promises.length;i++){
            promises[i].then(v => {
                // 得知对象的状态是成功的
                // 每个Promise对象都成功
                count++
                // 将当前 Promise 对象成功的结果,存入到数组中
                arr[i] = v
                if(count === promises.length){
                    resolve(arr)
                }
            },r => {
                reject(r)
            })
        }
    })
}

race 方法

const p3 = Promise.resolve('111')
const p4 = Promise.reject('222')
const p5 = Promise.resolve('333')

const race = Promise.race([p3,p4,p5])
console.log(race)

实现:

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

完整代码

function Promise(executor) {
  // 添加属性
  this.PromiseState = "pending";
  this.PromiseResult = null;

  this.callback = [];

  const _this = this;

  // resolve 函数
  function resolve(data) {
    // 判断状态
    if (_this.PromiseState !== "pending") return;
    // 1.修改对象的状态(promiseState)
    _this.PromiseState = "fulfilled";
    // 2.设置对象结果值(promiseResult)
    _this.PromiseResult = data;
    // 调用成功的回调
    setTimeout(() => {
      _this.callback.forEach((item) => {
        item.onResolved(data);
      });
    });
  }
  // reject 函数
  function reject(data) {
    if (_this.PromiseState !== "pending") return;
    _this.PromiseState = "rejected";
    _this.PromiseResult = data;
    // 调用失败的回调
    setTimeout(() => {
      _this.callback.forEach((item) => {
        item.onRejected(data);
      });
    });
  }

  try {
    // 同步调用 【执行器函数】
    executor(resolve, reject);
  } catch (e) {
    // 调用 reject 将状态变为 reject
    reject(e);
  }
}

// 添加 then 方法
Promise.prototype.then = function (onResolved, onRejected) {
  const _this = this;

  if (typeof onRejected !== "function") {
    onRejected = (reason) => {
      throw reason;
    };
  }
  if (typeof onResolved !== "function") {
    onResolved = (value) => {
      return value;
    };
  }

  return new Promise((resolve, reject) => {
    // 封装函数
    function callback(type) {
      try {
        // 获取回调的执行结果
        let result = type(_this.PromiseResult);
        if (result instanceof Promise) {
          // 如果返回的是 Promise
          result.then(
            (v) => {
              resolve(v);
            },
            (r) => {
              reject(r);
            }
          );
        } else {
          // 如果返回的是 非Promise 数据
          resolve(result);
        }
      } catch (e) {
        reject(e);
      }
    }

    if (this.PromiseState === "fulfilled") {
      setTimeout(() => {
        callback(onResolved);
      });
    }
    if (this.PromiseState === "rejected") {
      setTimeout(() => {
        callback(onRejected);
      });
    }
    if (this.PromiseState === "pending") {
      this.callback.push({
        onResolved: function () {
          callback(onResolved);
        },
        onRejected: function () {
          callback(onRejected);
        },
      });
    }
  });
};

// 添加 catch 方法
Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected);
};

// 添加 resolve 方法
Promise.resolve = function (value) {
  // 返回Promise对象
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(
        (v) => {
          resolve(v);
        },
        (r) => {
          reject(r);
        }
      );
    } else {
      resolve(value);
    }
  });
};

// 添加 reject 方法
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  });
};

// 添加 all 方法
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let count = 0;
    let arr = [];
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        (v) => {
          // 得知对象的状态是成功的
          // 每个Promise对象都成功
          count++;
          // 将当前 Promise 对象成功的结果,存入到数组中
          arr[i] = v;
          if (count === promises.length) {
            resolve(arr);
          }
        },
        (r) => {
          reject(r);
        }
      );
    }
  });
};

// 添加 race 方法
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        (v) => {
          resolve(v);
        },
        (r) => {
          reject(r);
        }
      );
    }
  });
};

async 和 await 语法糖

async 函数

async 其实就相当于 new Promise 的语法变式。

async function main(){}
let result = main()
console.log(result)

Snipaste_2022-09-03_14-15-35.png

可以看到,当一个函数加上了 async 之后,调用这个函数的实例就变成了一个 状态为成功,值为undefined 的 Promise

async function main(){
    /**
     * 第一种情况
     * 如果返回值是一个 非Promise 类型的数据
     * 那么后面打印的结果是 状态为resolve 值为返回的521 的 Promise
     */ 
    return 521

    /**
     * 第二种情况
     * 如果返回的死一个 Promise 对象
     * 那么后面打印的结果就是 状态和值为返回Promise的状态和值 的Promise
     * 比如本例中,打印的就是 状态为reject 值为Error 的Prmise
     */ 
    // return new Promise((resolve,reject) => {
    //     reject('Error')
    // })

    /**
     * 第三种情况
     * 如果抛出了一个异常
     * 那么打印出来的就是 状态为失败 值为抛出的值 oh no 的Promise
     */
    // throw 'oh no'
}

let result = main()

console.log(result)

可以看到,async 函数的返回结果和前面 then 方法的返回结果可以说是一模一样。

说的更明白一点:下面两个例子完全一致。

async function hd(){
    return "521";
}
hd.then(value => console.log(value));
new Promise(resolve=>{
    resolve("521");
}).then(value => console.log(value));

await 表达式

await 右侧的表达式一般为 Promise 对象,但是也可以是其他的值。

如果表达式是 Promise 对象,await 返回时 Promise 成功的值。

如果表达式时其他值,直接将此作为 await 的返回值。

async function main() {
    let p = new Promise((resolve, reject) => {
        resolve('ok')
    })

    let q = new Promise((resolve, reject) => {
        reject('Error')
    })

    // 1.右侧为 Promise 的情况
    let res1 = await p
    console.log(res1)
    // 2.右侧为其他类型的数据
    let res2 = await 20
    console.log(res2)
    // 3.如果 Promise 是失败的状态
    try {
        let res3 = await p
    }catch(err){
        console.log(err)
    }
}

main()

Snipaste_2022-09-03_17-08-24.png

注意:

  1. await 必须写在 async 函数中,但 async 函数中可以没有 await。
  2. 如果 await 的 Promise 失败了,就会抛出异常,需要通过 try...catch 捕获处理。

用 async 和 await 结合读取 fs 模块

const fs = require("fs");
const util = require("util");

const minReadFile = util.promisify(fs.readFile);

async function main() {
  try {
    // 读取第一个文件的内容
    let data1 = await minReadFile("./fs/1.txt");
    let data2 = await minReadFile("./fs/2.txt");
    let data3 = await minReadFile("./fs/3.txt");

    console.log(data1 + data2 + data3);
  } catch (err) {
    console.log(err);
  }
}

main();

用 async 和 await 结合发送 ajax 请求

function sendAJAX(method, url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(method, url)
        xhr.send()
        // 处理结果
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.response)
                } else {
                    reject(xhr.status)
                }
            }
        }
    })
}

let btn = document.querySelector('#btn')

btn.addEventListener('click',async function(){
    let posts = await sendAJAX('GET','http:/127.0.0.1:3000/posts/1')

    console.log(posts)
})

class 与 await 相结合

function sendAJAX(method, url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(method, url)
        xhr.send()
        // 处理结果
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.response)
                } else {
                    reject(xhr.status)
                }
            }
        }
    })
}

class Posts {
    constructor(id){
        this.id = id;
    }
    // 如果类中有 then 方法,那么 await 会查找类中的then方法
    then(resolve,reject){
        let post = sendAJAX('GET',`http://localhost:3000/posts/${this.id}`)
        resolve(post)
    }
}

async function get() {
    let post = await new Posts('1');
    console.log(post)
}

get()

如果我的类中有一个 then 方法,那么该类返回的是一个 Promise ,像上面的例子中,如果没有 resolve(post) 这个语句,那么上面都打印不出来;又如果改为 resolve() ,那么下面打印的是 undefined。

当然异步也可以封装在类的内部。

Snipaste_2022-09-06_11-13-54.png

class Posts {
    async get(id){
        let post = await sendAJAX('GET',`http://127.0.0.1:3000/posts/${id}`)
        console.log(post)
        return post
    }
}

new Posts().get(1).then(value => {
    console.log(value)
},reason => {
    console.log(resaon)
})

并行执行技巧

环境搭建

function p1() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve("zs");
        }, 2000);
    });
}

function p2() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve("ls");
        }, 2000);
    });
}
async function hd() {
    let h1 = await p1();
    let h2 = await p2();
}

向上面这样,会先执行p1,后执行p2。

async function hd(){
    let h1 = p1();
    let h2 = p2(); 
    let h1value = await h1;
    let h2value = await h2;
}

这样执行,p1和p2peye.com是异步,同时执行。

async function hd(){
    let res = await Promise.all([p1(),p2()]);
    console.loh(res);
}

当然,也可以更简单一点,直接调用 Promise.all 这个api。

参考文献及视频

www.bilibili.com/video/BV1Bd…

www.bilibili.com/video/BV15J…

www.bilibili.com/video/BV1GA…

结语

好啦,本次分享就到这里。

文章如果有不正确的地方,欢迎指正,共同学习,共同进步。

若有侵权,请联系作者删除。