时隔多年,还是要手撸一个Promise,不撸不行的那种

1,013 阅读15分钟

前言

大家好,我是青年野味,相信这么多年的开发过程中,大家都对Promise或多或少的了解,但是在面对一些面试题时时犹豫不决,原因终究还是不了解Promise的原理,今天带着大家用易理解的方式一起来实现一下,这样我自己也能得到,同时提升希望能帮助的到大家,如果大家有建议或者发现问题的都可以在下面评论中指出,我也会积极更正过来的

认识Promise

Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

我相信大家经常写这样的代码:

// 当参数a大于10且参数fn2是一个方法时 执行fn2
function fn1(a, fn2) {
    if (a > 10 && typeof fn2 == 'function') {
        fn2()
    }
}
fn1(11, function() {
    console.log('this is a callback')
})复制代码

一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。

这时候我们的promise就应运而生、粉墨登场了

promise是用来解决两个问题的:

  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的

Promise API

Promise对象是一个构造函数,用来生成Promise实例的,自身拥有着我们平时用的all、resolve、reject等熟悉的方法,原型上有着then、catch等一样熟悉的的方法。

new Promise

Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:

  • resolve :异步操作执行成功后的回调函数

  • reject:异步操作执行失败后的回调函数

说走就走,让我们来new一个Promise实例吧

let p = new Promise((resolve, reject) => {
    console.log("青年野味new了一个Promise")
    resolve("我成功了")
    //reject('我失败了');
});
//resolve时
console.log("p",p) //p Promise {<fulfilled>: '我成功了'}

//reject时
console.log("p",p) //p Promise {<rejected>: '我失败了'},控制台会抛出一个错误

//同时执行resolve和reject时
console.log("p",p) //p Promise {<fulfilled>: '我成功了'}

// throw时
let p2 = new Promise((resolve, reject) => { throw('我报错了') })


根据上面的情况我们总结一下:

  • 创造promise实例后,它会立即执行。

  • 当执行了resolve,Promise状态会变成fulfilled,即成功状态

  • 执行了reject,Promise状态会变成rejected,即失败状态

  • resolve和reject同时存在时,Promise只以第一次为准,第一次成功就永久fulfilled,第一次失败就永远状态为rejected,状态一旦改变,就不会再变。

  • 当Promise中有throw的话,就相当于执行了reject

那么我们就把这个四种情况一一实现了。

class yw_Promise {
    constructor(executor) {
        //默认状态为padding
        this.state = "pending"
        this.result = null
        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (data) => {
            if (this.state !== "pending") return
            this.state = "fulfiled"
            this.result = data
            //当我们数组里边没有时就不用去执行
            while (this.onResolvedCallbacks.length) {
                this.onResolvedCallbacks.shift()(data)
            }
        }

        const reject = (data) => {
            if (this.state !== "pending") return
            this.state = "rejected"
            this.result = data
            //当我们数组里边没有时就不用去执行
            while (this.onRejectedCallbacks.length) {
                this.onRejectedCallbacks.shift()(data)
            }
        }

        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }

    }
}

const yw_p = new yw_Promise((resolve, reject) => {
    // resolve("初次见面,成功了")
    // reject("初次见面,失败了")
    throw("初次见面,失败了")
})

console.log(yw_p) //yw_Promise {state: 'rejected', result: '初次见面,失败了'}

then

then是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。then方法返回的是一个新的Promise实例。

平时我们使用then是这样的:

//立即输出"OK"
let p = new Promise((resolve, reject) => {
    resolve('OK');
}).then(value => console.log(value) , (err) => console.error(err) );;


// 1秒后输出 ERROR
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('ERROR')
    }, 1000)
}).then(res => console.log(res), err => console.log(err))

// 链式调用 输出 20
let p3 = new Promise((resolve, reject) => {
        resolve(10)
    }).then(res => 2 * res, err => console.log(err))
    .then(res => console.log(res), err => console.log(err))

我们来总结一下上面的知识点:

  • then函数中有两个回调参数,一个是成功的回调,一个是失败的回调
  • 当Promise状态为fulfilled执行成功回调,为rejected执行失败回调
  • reject或者resolve在定时器中时,则等定时器执行完才再执行then
  • then链式调用时,下一次then执行时受上一次then返回值的影响

根据上面的知识点,我们一一来实现吧

class yw_Promise {
    ...
    then(onResolved, onRejected){
        if(this.state === "fulfiled"){
            onResolved(this.value)
        }
        if(this.state === "rejected"){
            onRejected(this.value)
        }
    }
}

以为就这样就完了吗?那就大错特错了,上面都没有考虑到定时器和链式调用的情况呢,首先定时器情况是异步的,又因为我们需要等定时器执行完then方法才会执行,我们可以来详细看下这个定时器时候Promise的状态。

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

console.log("同步输出p",p) //同步输出p Promise {<pending>}

setTimeout(()=>{
    console.log("异步输出p",p) //异步输出p Promise {<fulfilled>: 'OK'}
},2000)

当我们同步的时候发现Promise的状态还是pending,到了2秒后我们再输出就变成了fulfilled状态了。

那么问题就来我怎么知道它到底是什么时候变成了fulfilled状态?其实很简单,看定时器首先是不是要跑完,跑完了是不是要定时器里边的resolve或者是reject,这两个我们在constructor中写的是同步的吧,这两个一执行我们是不是就可以把状态改成相应的状态了,那状态肯定就不是pending,而是fulfilled或者rejected

由此then中判断如果Promise状态是pending,那这时候我们可以把每次调用resolve的结果存入一个数组中,每次调用reject的结果存入一个数组。

到这里还是很好理解的,但是在后面这一步的时候就有点难懂了。假设我现在存了有两个数组分别是onResolvedCallbacks、onRejectedCallbacks,我们的定时器也到点要跑完了,这时候要去执行一开始里边写的resolve('OK');,那你是不是要把之前存进去的回调结果一一执行了,那要怎么执行呢,从后面开始执行还是从前面开始执行。正确答案是从数组的下标0开始一一执行,因为我们要有先后顺序的排队依次出来。

根据上面说的我们来完善一下

class yw_Promise {
    constructor(executor) {
        //默认状态为padding
        this.state = "padding"
        this.result = undefined
        //存放成功的回调
        this.onResolvedCallbacks = []
        //存放失败的回调
        this.onRejectedCallbacks = []
        const resolve = (data) => {
            if (this.state !== "padding") return
            this.state = "fulfiled"
            this.result = data
            //当我们数组里边没有时就不用去执行
            while (this.onResolvedCallbacks.length) {
                this.onRejectedCallbacks.shift()(data)
            }
        }

        const reject = (data) => {
            if (this.state !== "padding") return
            this.state = "rejected"
            this.result = data
            //当我们数组里边没有时就不用去执行
            while (this.onRejectedCallbacks.length) {
                this.onRejectedCallbacks.shift()(data)
            }
        }

        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }

    }
    then(onFulFilled, onRejected) {
        if (this.state === "fulfiled") {
            onResolved(this.result)
        }
        if (this.state === "rejected") {
            onRejected(this.result)
        }

        // 当前既没有完成 也没有失败
        if (this.state === 'pending') {
            // 存放成功的回调
            this.onResolvedCallbacks.push(() => {
                onFulFilled(this.result);
            });
            // 存放失败的回调
            this.onRejectedCallbacks.push(() => {
                onRejected(this.result);
            });
        }
    }
}

又以为这就完了,那你有错了,还有链式调用和定时器的情况呢,一样举个简单的例子给大家做介绍

let p = new Promise((resolve, reject) => {
    resolve('OK');
}).then(value => {
    // console.log(value)
    return new Promise((resolve, reject) => {
        resolve("success");
    });
},err=>console.error(err)).then(value => {
    console.log(value);
},err=>console.error(err)).then(value => {
    console.log(value);
},err=>console.error(err))


setTimeout(()=>{
console.log("p",p) //p Promise {<fulfilled>: undefined}
},2000)

console.log("p",p) 
//p Promise {<pending>}
//这里为什么会输出状态pending呢,原因是then是微任务

根据上面的例子我们总结一下知识点:

  • then方法本身会返回一个新的Promise对象

  • 如果返回值是Promise对象,返回值为成功,新promise就是成功,返回值为失败,新Promise就是失败

  • 如果返回值非Promise对象,新promise对象就是成功,值为此返回值

根据上面的总结的知识点,完善一下then

class yw_Promise {
    ....
    then(onFulFilled, onRejected) {
        //防止值得穿透 
        onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
        onRejected = typeof onRejected === 'function' ? onRejected : err => {
            throw err;
        }
        //作为下一次then方法的promise
        let promise2 = new yw_Promise((resolve, reject) => {

            const resolvePromise = resultCallback => {
                setTimeout(() => {
                    try {
                        const x = resultCallback(this.result)
                        
                        if (x === promise2) {
                            //不能自己等待自己完成
                            return reject(new TypeError('循环引用'));
                        }
                        if (x instanceof yw_Promise) {
                            x.then(resolve, reject)
                        } else {
                            // 非Promise就直接成功
                            resolve(x)
                        }
                    } catch (err) {
                        // 处理报错
                        reject(err)
                    }
                })

            }

            if (this.state === "fulfiled") {

                resolvePromise(onFulFilled)

            }
            if (this.state === "rejected") {

                resolvePromise(onRejected)

            }

            // 当前既没有完成 也没有失败
            if (this.state === 'pending') {
                // 存放成功的回调
                this.onResolvedCallbacks.push(() =>
                    resolvePromise(onFulFilled)
                );
                // 存放失败的回调
                this.onRejectedCallbacks.push(() =>
                    resolvePromise(onRejected)

                );

            }

        })

        return promise2

    }
}

注意:因为实际上的then是个微任务,但这里手写的then方法实际上是一个异步的宏任务来的,因为本人自己无法实现真正意义上的微任务,就这样写了一个setTimeout来代替,如果各位大佬有什么好的建议的,带带弟弟。

2021-12-12 131817.gif

以上then方法就算是完成了,但实际上如果你自己所定义的Promise需要和别人的Promise进行交互时,上面的resolvePromise就需要重写。方法中传入四个参数,分别是promise2,x,resolve,reject,promise2指的是上一次返回的promise,x表示运行promise返回的结果,resolve和reject是promise2的方法。则代码写为:

function resolvePromise(promise2, x, resolve, reject) {
    //判断x是不是promise
    //规范中规定:我们允许别人乱写,这个代码可以实现我们的promise和别人的promise 进行交互
    if (promise2 === x) { //不能自己等待自己完成
        return reject(new TypeError('循环引用了'));
    };
    // x是除了null以外的对象或者函数
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        let called; //防止成功后调用失败
        try { //防止取then是出现异常  object.defineProperty
            let then = x.then; //取x的then方法 {then:{}}
            if (typeof then === 'function') { //如果then是函数就认为他是promise
                //call第一个参数是this,后面的是成功的回调和失败的回调
                then.call(x, y => { //如果Y是promise就继续递归promise
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject)
                }, r => { //只要失败了就失败了
                    if (called) return;
                    called = true;
                    reject(r);
                });
            } else { //then是一个普通对象,就直接成功即可
                resolve(x);
            }
        } catch (e) {
            if (called) return;
            called = true;
            reject(e)
        }
    } else { //x = 123 x就是一个普通值 作为下个then成功的参数
        resolve(x)
    }

}

//then中使用可以交互的resolvePromise方法
then(){
...
    if (this.state === "fulfiled") {
        let x = onFulFilled(this.result);
        resolvePromise(promise2,x,resolve,reject)
    }
    if (this.state === "rejected") {
        let x = onRejected(this.result);
        resolvePromise(promise2,x,resolve,reject)
    }

    // 当前既没有完成 也没有失败
    if (this.state === 'pending') {
        // 存放成功的回调
        this.onResolvedCallbacks.push(() => {
            let x = onFulFilled(this.result);
            return resolvePromise(promise2,x,resolve,reject)
        });
        // 存放失败的回调
        this.onRejectedCallbacks.push(() => {
            let x = onRejected(this.result);
            return resolvePromise(promise2,x,resolve,reject)
        });
    }
}

catch

catch其实本身就是一个then方法来的,只不过只有reject回调参数resolve是undefined

//catch 方法
catch (onRejected) {
    return this.then(undefined, onRejected);
}

resolve和reject

这里的resolve和reject并非是构造函数中接收的参数,但是跟接收参数中异步操作成功后的回调有着很相似的作用。

让我们来看一下,它们是怎么用的:

//第一种:
let p1 = Promise.resolve("青年野味来搞Primise了")
console.log("p1",p1) //p1 Promise {<fulfilled>: '青年野味来搞Primise了'}

//第二种:
let p2 = Promise.resolve(new Promise((resolve, reject) => {
    // resolve('OK');
    reject('Error');
}));
//reject时
console.log("p2",p2); //p2 Promise {<rejected>: 'Error'}

//resolve时
console.log("p2",p2); //p2 Promise {<fulfilled>: 'OK'}

resolve和reject方法中,当我们使用时分两种情况:

  • 如果传入的参数为非Promise类型的对象, 则返回的结果为相应状态的promise对象(resolve为fulfilled状态,reject为rejected状态

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

那让我们一起实现第一种情况先


//class中的静态方法
static resolve(data) {
     return new yw_Promise((resolve, reject) => resolve(data))
}
static reject(data) {
    return new yw_Promise((resolve, reject) => reject(data))
}

第二种情况当我们参数是Promise呢,如果使用上面的,结果里面就会返回一个Promise,所以我们要将上面的代码完善一下。

//完整版
//class中的静态方法
static resolve(value) {
    return new yw_Promise((resolve, reject) => {
        if (value instanceof yw_Promise) {
            value.then(v => {
                resolve(v);
            }, r => {
                reject(r);
            })
        } else {
            //状态设置为成功
            resolve(value);
        }
    });
}
static reject(value) {
    return new yw_Promise((resolve, reject) => {
        if (value instanceof yw_Promise) {
            value.then(v => {
                resolve(v);
            }, r => {
                reject(r);
            })
        } else {
            //状态设置为成失败
            reject(value);
        }
    });
}

resolve和reject方法也就算完成了,也是跟then方法一样,并不能跟别人的Promise做交互的。因为都用了instanceof,举个例子: A instanceOf B,判断A是否经过B的原型链。因为别人的Promise并不在我们自己写的Promise原型链的,所以判断结果都是false的,有兴趣的朋友可以自己研究一下。

all

想要有很多的朋友在开发中都会遇到,我有多个接口要发请求,但是我想依次按顺序进行发送请求,这个时候就可以用到all方法

让我们来看看他的用法


const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return new Promise((resolve,reject)=>resolve(id));
});

Promise.all(promises).then(function (posts) {
  console.log(posts) // [2, 3, 5, 7, 11, 13]
}).catch(function(reason){
  // ...
});

const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
if(id !== 3) return new Promise((resolve,reject)=>resolve(id));
  return new Promise((resolve,reject)=>reject(id))
});

Promise.all(promises).then(function (posts) {
    //...
}).catch(function(reason){
  console.log(reason) //3
});

const promises = [2, 3, 5, 7, 11, 13]

Promise.all(promises).then(function (posts) {
  console.log(posts) //[2, 3, 5, 7, 11, 13]
}).catch(function(reason){
  console.log(reason) //此处并没有输出
});

根据上面的我们来总结一下知识点:

  • 如果所有Promise都成功,则返回成功结果数组
  • 如果有一个Promise失败,则返回这个失败结果
  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功

那就让我们来实现一下吧


//添加 all 方法
static all(promises) {
    //返回结果为promise对象
    return new yw_Promise((resolve, reject) => {
        //声明变量
        let count = 0;
        let arr = [];
        //遍历
        for (let i = 0; i < promises.length; i++) {
            if (promises[i] instanceof yw_Promise) {
                promises[i].then(v => {
                    //得知对象的状态是成功
                    //每个promise对象 都成功
                    count++;
                    //将当前promise对象成功的结果 存入到数组中
                    arr[i] = v;
                    //判断
                    if (count === promises.length) {
                        //修改状态
                        resolve(arr);
                    }
                }, r => {
                    reject(r);
                });
            }

        }
    });
}

race

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 哪个Promise最快得到结果,就返回那个结果,无论成功失败
//添加 race 方法
static race(promises) {
    return new yw_Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            if (promises[i] instanceof yw_Promise) {
                promises[i].then(v => {
                    //修改返回对象的状态为 『成功』
                    resolve(v);
                }, r => {
                    //修改返回对象的状态为 『失败』
                    reject(r);
                })
            }

        }
    });
}

allSettled

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 把每一个Promise的结果,集合成数组,返回
static allSettled(promises) {
    return new yw_Promise((resolve, reject) => {
        //声明变量
        let count = 0;
        let arr = [];
        const addData = (status, value, i) => {
            arr[i] = {
                status,
                value
            }
            count++
            if (count === promises.length) {
                resolve(arr)
            }
        }
        //遍历
        for (let i = 0; i < promises.length; i++) {
            if (promises[i] instanceof MyPromise) {
                promises[i].then(v => {
                    addData('fulfilled', v, i)
                }, r => {
                    addData('rejected', r, i)
                });
            } else {
                addData('fulfilled', promises[i], i)
            }

        }
    });
}

any

any与all相反

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 如果有一个Promise成功,则返回这个成功结果
  • 如果所有Promise都失败,则报错
static any(promises) {
    return new Promise((resolve, reject) => {
        let count = 0
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(val => {
                resolve(val)
            }, err => {
                count++
                if (count === promises.length) {
                    reject(new AggregateError('All promises were rejected'))
                }
            })
        }
    })
}

自定义Promise完整代码,细品

class yw_Promise {
    constructor(executor) {
        //默认状态为padding
        this.state = "pending"
        this.result = null
        this.onResolvedCallbacks = []
        this.onRejectedCallbacks = []
        const resolve = (data) => {
            if (this.state !== "pending") return
            this.state = "fulfiled"
            this.result = data
            //当我们数组里边没有时就不用去执行
            while (this.onResolvedCallbacks.length) {
                this.onResolvedCallbacks.shift()(data)
            }
        }

        const reject = (data) => {
            if (this.state !== "pending") return
            this.state = "rejected"
            this.result = data
            //当我们数组里边没有时就不用去执行
            while (this.onRejectedCallbacks.length) {
                this.onRejectedCallbacks.shift()(data)
            }
        }

        try {
            executor(resolve, reject)
        } catch (e) {
            reject(e)
        }

    }
    then(onFulFilled, onRejected) {
        //防止值得穿透 
        onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : y => y;
        onRejected = typeof onRejected === 'function' ? onRejected : err => {
            throw err;
        }
        //作为下一次then方法的promise
        let promise2 = new yw_Promise((resolve, reject) => {

            const resolvePromise = resultCallback => {
                setTimeout(() => {
                    try {
                        const x = resultCallback(this.result)
                        
                        if (x === promise2) {
                            //不能自己等待自己完成
                            return reject(new TypeError('循环引用了'));
                        }
                        if (x instanceof yw_Promise) {
                            x.then(resolve, reject)
                        } else {
                            // 非Promise就直接成功
                            resolve(x)
                        }
                    } catch (err) {
                        // 处理报错
                        reject(err)
                    }
                })

            }

            if (this.state === "fulfiled") {

                resolvePromise(onFulFilled)

            }
            if (this.state === "rejected") {

                resolvePromise(onRejected)

            }

            // 当前既没有完成 也没有失败
            if (this.state === 'pending') {
                // 存放成功的回调
                this.onResolvedCallbacks.push(() =>
                    resolvePromise(onFulFilled)
                );
                // 存放失败的回调
                this.onRejectedCallbacks.push(() =>
                    resolvePromise(onRejected)

                );

            }

        })

        return promise2

    }

    //catch 方法
    catch (onRejected) {
        return this.then(undefined, onRejected);
    }
    //class中的静态方法
    static resolve(value) {
        return new yw_Promise((resolve, reject) => {
            if (value instanceof yw_Promise) {
                value.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            } else {
                //状态设置为成功
                resolve(value);
            }
        });
    }
    static reject(value) {
        return new yw_Promise((resolve, reject) => {
            if (value instanceof yw_Promise) {
                value.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            } else {
                //状态设置为成功
                reject(value);
            }
        });
    }

    //添加 all 方法
    static all(promises) {
        //返回结果为promise对象
        return new yw_Promise((resolve, reject) => {
            //声明变量
            let count = 0;
            let arr = [];
            //遍历
            for (let i = 0; i < promises.length; i++) {
                if (promises[i] instanceof MyPromise) {
                    promises[i].then(v => {
                        //得知对象的状态是成功
                        //每个promise对象 都成功
                        count++;
                        //将当前promise对象成功的结果 存入到数组中
                        arr[i] = v;
                        //判断
                        if (count === promises.length) {
                            //修改状态
                            resolve(arr);
                        }
                    }, r => {
                        reject(r);
                    });
                }

            }
        });
    }

    //添加 race 方法
    static race(promises) {
        return new yw_Promise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                if (promises[i] instanceof MyPromise) {
                    promises[i].then(v => {
                        //修改返回对象的状态为 『成功』
                        resolve(v);
                    }, r => {
                        //修改返回对象的状态为 『失败』
                        reject(r);
                    })
                }

            }
        });
    }

    static allSettled(promises) {
        return new yw_Promise((resolve, reject) => {
            //声明变量
            let count = 0;
            let arr = [];
            const addData = (status, value, i) => {
                arr[i] = {
                    status,
                    value
                }
                count++
                if (count === promises.length) {
                    resolve(arr)
                }
            }
            //遍历
            for (let i = 0; i < promises.length; i++) {
                if (promises[i] instanceof MyPromise) {
                    promises[i].then(v => {
                        addData('fulfilled', v, i)
                    }, r => {
                        addData('rejected', r, i)
                    });
                } else {
                    addData('fulfilled', promises[i], i)
                }

            }
        });
    }

    static any(promises) {
        return new Promise((resolve, reject) => {
            let count = 0
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(val => {
                    resolve(val)
                }, err => {
                    count++
                    if (count === promises.length) {
                        reject(new AggregateError('All promises were rejected'))
                    }
                })
            }
        })
    }
}

拓展

有想了解跟多Promise相关Api的可以去这里看

结语

过程总是令人烦恼的,但是结果总是出乎意外的令人的兴奋。希望这个可以帮助到大家,觉得不错的可以点赞加收藏,觉得其中写的有问题的也可以在下方评论区告诉我,让我们一直在进步的路上