异步编程-学习笔记

一、认识异步编程

1.同步模式和异步模式

Javascript设计初衷是为了在浏览器里满足网页交互的需要,所以设计为单线程模式,简单、安全,但如果某段代码执行很费时,就会造成后面代码的延迟执行(页面假死)。于是Js将任务的执行模式分成两种:同步模式、异步模式。

同步模式的执行顺序就是排队执行,前面的执行完毕,后面的再执行。

异步模式则是开启一个任务后,不会等待任务执行完毕才开始下一个任务,而是开启一个任务后就开始下一个任务。

同步或异步是说,执行环境提供的API是以同步还是异步的模式去工作。

2.异步模式图解

下图一的代码中,setTimeout 这个异步模式工作的API,其功能就是在某段时间后,向Javascirpt的消息队列中push一个任务(回调函数),当时机到了,就去执行回调函数。本段代码中,除setTimeout是异步模式工作的,其它的代码都是同步模式工作的。

2.1 EventLoop(事件循环机制)

Javascript的执行是单线程的,就是说一个时刻只能执行一个任务(一句代码),将要执行的代码会被放入执行栈(Call stack),每执行完毕一句,就从栈中清除。

如果遇到像setTimeout、setInterval这些异步执行的API,则会把其回调函数(按设定的时机)push入消息队列(就是待执行的任务队列)。

当目前执行栈(Call stack)中的代码都执行完毕,则EventLoop(事件循环机制)就会从消息队列中取出最早入队的任务,并把该任务代码放入执行栈,然后开始执行。

当执行栈内代码再次执行完毕,EventLoop(事件循环机制)就继续从消息队列中取出最早入队的任务,去执行。直至所有消息队列中的任务全部执行完毕。

简而言之:EventLoop就是在执行栈当前执行的代码运行完毕后,再去从消息队列中取出最早入队的一个任务执行,每个任务依次执行,直至全部任务执行完毕。


2.2 消息队列

消息队列用于存放异步模式API们的回调函数。每个回调函数都是一个待执行任务(一段待执行代码)。


2.3 宏任务和微任务

消息队列中的每个任务,都是宏任务,宏任务需要等待前面的宏任务依次执行后,(当EventLoop循环轮到自身时)自身再执行,setTimeout方法接收的回调函数和Dom事件回调就会作为宏任务去消息队列排队等待。
而为了提高效率,浏览器API中的Promise回调、MutationObserver和node环境的API process.nextTick 都会作为微任务执行,意思就是在当前执行栈中的代码一执行完毕,就立即执行(作为当前执行任务的微任务),而不用到消息队列去排队等待。

二、回调函数

回调函数是所有异步编程模式的基础,回调函数可以理解为一件很明确的想要做的事情,但是需要等待某个时机成熟才去做(执行)。

三、Promise的使用

一个Promise类型的对象,用于描述一个异步任务的最终结果。
Promse使用示例:

// Promise基本用法
const promise = new Promise(function (resolve, reject){
    resolve(100) // 承诺达成
    // reject(new Error('promise rejected')) // 承诺失败
})

promise.then(function(value){ // then接收的方法会进入回调队列
    console.log('resolved:', value)
}, function(err){
    console.log('rejected', err)
})

console.log('end') // end会先于上面promise的回调执行

/*
输出结果:
end
resolved: 100
*/

Promise本质上也是使用回调函数来定义异步任务结束后所需要执行的任务,嵌套使用的方式是使用Promise的常见错误

可借助于Promise then方法的链式调用的特点,保证异步任务的扁平化。

Promise对象的then 方法会返回一个全新的promise对象,后面的then方法就是在为前一个then 返回的Promise 注册回调方法,前面 then 方法中回调函数的返回值会作为后面 then 方法回调的参数。如果回调中返回的是Promise,那后面的then 方法的回调会等待它的结束。

Promise链式调用示例:

// Promise的链式调用示例
const promise = new Promise(function(resolve, reject){
    resolve('111')
}).then((value) => {
    console.log('222')
    console.log(value)
    return 'gagaga'
}).then((value) => {
    console.log('333')
    console.log(value)
})

/*
输出结果:
222
111
333
gagaga
*/

Promise.all静态方法的使用示例:

// Promise.all静态方法 按照异步任务的调用顺序获得各个任务的结果
function p1(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('p1')
        }, 2000)
    })
}
function p2(){
    return new Promise((resolve, reject) => {
        resolve('p2')
    })
}
Promise.all(['a', 'b', p1(), p2(), 'c'])
.then(values => {console.log('values: ', values)}, reason => console.log(reason))

/*输出结果(2秒后输出):
values:  [ 'a', 'b', 'p1', 'p2', 'c' ]
*/


Promise.resolve 方法,其作用就是把一个普通的值转换为promise对象;如果参数就是promise对象,则直接返回该对象。

Promise.resolve静态方案使用示例:

// Promise.resolve方法
function p1(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('p1 lalala')
        }, 2000)
    })
}

Promise.resolve(50)
    .then(value => console.log(value)) // 相当于 .then(console.log)

Promise.resolve(p1())
    .then(console.log)

/*
输出结果(立即输出第一行,2秒后输出第二行):
50
p1 lalala
*/

四、Generator异步方案和Async/Await

1. Generator异步方案

ES2015提供了Generator 生成器函数,允许一个函数分段执行。
A.生成器函数示例:

// Generator函数生成器示例1
function * generateId(){
    let index = 0
    while(true){
        index++
        yield index
    }
}
const geneId = generateId()

const r1 = geneId.next()
console.log('r1: ', r1)

setTimeout(() => {
    const r2 = geneId.next().value
    console.log('r2: ', r2)
}, 1000)

/*
输出结果:
r1:  { value: 1, done: false }
r2:  2
*/

 B.假如有一个多步骤的生成器函数,需要逐个执行其中的每个异步步骤,则需要一个生成器函数的自动执行器:
生成器函数的自动执行器示例:

/*
Generator函数生成器的 自动执行器
*/

function ajax(type){
    return new Promise((resolve, reject) => {
        if(type == 'users'){
            setTimeout(() => resolve(['Andy', 'Lily']), 2000)
        } else if(type == 'posts'){
            setTimeout(() => resolve(['Street', 'Street2']), 3000)
        }
    })
}

function * main(){
    try{
        const users = yield ajax('users')
        console.log('users: ', users)

        const posts = yield ajax('posts') // 需等待获取到users之后再执行
        console.log('posts: ', posts)
    } catch(e){
        console.log(e)
    }
}

//生成器函数的自动执行器
function co(generator){
    const g = generator()

    function handleResult(result){
        if(result.done) return
        result.value.then(data => {
            handleResult(g.next(data))
        }, error => {
            g.throw(error)
        })
    }

    handleResult(g.next())
}
co(main)

/*
输出结果(2秒后输出users,再过3秒输出posts):
users:  [ 'Andy', 'Lily' ]
posts:  [ 'Street', 'Street2' ]
*/

2.Async/Await - 语法层面的异步编程方案

相比Generator函数生成器,Async/Await 不需要手动去执行。
Async函数的返回值是一个promise对象,方便对整体流程的控制。
Await目前只能用于有Async描述的方法中。
Async/Await异步编程示例(将上例中的函数改为Async描述,yield关键词改为await即可):

// Async/Await异步编程示例
function ajax(type){
    return new Promise((resolve, reject) => {
        if(type == 'users'){
            setTimeout(() => resolve(['Andy', 'Lily']), 2000)
        } else if(type == 'posts'){
            setTimeout(() => resolve(['Street', 'Street2']), 3000)
        }
    })
}

async function main(){
    try{
        const users = await ajax('users')
        console.log('users: ', users)
        const posts = await ajax('posts')
        console.log('posts: ', posts)
    } catch(e){
        console.log(e)
    }
}

const promise = main()
promise.then(() => {
    console.log('整体结束')
})

/*
输出结果(2秒后输出users,再过3秒输出posts和整体结束):
users:  [ 'Andy', 'Lily' ]
posts:  [ 'Street', 'Street2' ]
整体结束
*/

五、手写Promise源码

1.对Promise的分析

const promise = new Promise((resolve, reject) => {
    resolve('成功的值')
    reject('失败原因')
})
promise.then(value => {
    console.log(value)
}, reason => {
    console.log(reason)
})

1. Promise是一个类 这个类接收一个执行器(函数) 执行器会立即执行

    这个执行器有两个参数:分别是resolve方法和reject方法

2. Promise有三种状态:成功-fulfilled 失败-rejected 等到-pending

    pending -> fulfilled

    pending -> rejected

一旦状态确定就不能更改

3. resolve 和 reject 函数是用来更改状态的

    resolve: fulfilled

    reject: rejected

4. then方法内部做的事就是判断当前Promise的状态

    如果状态是成功则执行成功回调函数

    如果状态时失败则执行失败回调函数

2.实现MyPromise核心

Step1:手写MyPromise-核心  构造器接收一个执行器函数 对象内部状态 resolve/reject方法 then方法

// Step1:手写MyPromise-核心  构造器接收一个执行器函数 对象内部状态 resolve/reject方法 then方法

// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor){                   // new MyPromise() 接收一个执行器函数
        executor(this.resolve, this.reject)  // 执行器函数有两个参数:resolve和reject 是内部方法
    }

    status = PENDING                         // 标识该promise对象的状态
    value = undefined                        // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
    reason = undefined                       // reject方法接收的参数:如果对象失败了,那么失败的原因是什么

    resolve = value => {                     // 用于把promise对象的状态更改为fulfilled-成功
        if(this.status !== PENDING) return;  // promise对象状态只能由pending-等待 变成 成功或失败
        this.status = FULFILLED
        this.value = value                   // 成功了的数据保存在this.value中 后续传递给成功的回调函数
    }
    reject = reason => {                     // 用于把promise对象的状态更改为rejected-失败
        if(this.status !== PENDING) return;
        this.status = REJECTED
        this.reason = reason                 // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
    }

    then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
        if(this.status == FULFILLED){
            successCallback(this.value)
        } else if(this.status == REJECTED){
            failCallback(this.reason)
        }
    }
}

// 测试1
const promise1 = new MyPromise((resolve, reject) => {
    resolve(100)
})
promise1.then(val => {
    console.log('success value: ', val)
}, rea => {
    console.log('fail reason: ', rea)
})
/*
输出结果(立即输出): - 符合预期
success value:  100
*/


// 测试2
const promise2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(100)
    }, 2000)
})
promise2.then(val => {
    console.log('success value: ', val)
}, rea => {
    console.log('fail reason: ', rea)
})
/*
输出结果 什么都没有输出:- 不符合预期
这是因为调用then方法的时候,promise对象还没有变成终态,所以then方法中还需要处理当前不是终态的情况
*/

由于上述这个思路代码,不满足promise状态延迟改变(异步)的需要,所以改写为如下:

Step2: 手写MyPromise-核心2  then方法调用时可能还未得到promise的终态

// 手写MyPromise-核心2 then方法调用时可能还未得到promise的终态

// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor){                   // new MyPromise() 接收一个执行器函数
        executor(this.resolve, this.reject)  // 执行器函数有两个参数:resolve和reject 是内部方法
    }

    status = PENDING                         // 标识该promise对象的状态
    value = undefined                        // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
    reason = undefined                       // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
    successCallback = undefined
    failCallback = undefined

    resolve = value => {                     // 用于把promise对象的状态更改为fulfilled-成功
        if(this.status !== PENDING) return;  // promise对象状态只能由pending-等待 变成 成功或失败
        this.status = FULFILLED
        this.value = value                   // 成功了的数据保存在this.value中 后续传递给成功的回调函数
        this.successCallback && this.successCallback(this.value) // 如果有保存而未执行的成功回调 则此时执行
    }
    reject = reason => {                     // 用于把promise对象的状态更改为rejected-失败
        if(this.status !== PENDING) return;
        this.status = REJECTED
        this.reason = reason                 // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
        this.failCallback && this.failCallback(this.reason) // 如果有保存而未执行的失败回调 则此时执行
    }

    then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
        if(this.status == FULFILLED){
            successCallback(this.value)
        } else if(this.status == REJECTED){
            failCallback(this.reason)
        } else {                             // 调用then方法时,promise还没有得到终态
                                             // 要等到其得到终态后,再执行成功或失败的回调
                                             // 先把回调都存储起来
            this.successCallback = successCallback
            this.failCallback = failCallback
        }
    }
}

// 测试1
const promise1 = new MyPromise((resolve, reject) => {
    resolve(100)
})
promise1.then(val => {
    console.log('success value: ', val)
}, rea => {
    console.log('fail reason: ', rea)
})
/*
输出结果(立即输出): - 符合预期
success value:  100
*/

// 测试2
const promise2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(100)
    }, 2000)
})
promise2.then(val => {
    console.log('success value: ', val)
}, rea => {
    console.log('fail reason: ', rea)
})
/*
输出结果(2秒后输出):- 符合预期
success value:  100
*/

  promise对象的then方法可以多次调用:

// promise对象的then方法可以多次调用
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功的值')
    }, 1500)
})
promise.then(value => {
    console.log('value1 :', value)
})
promise.then(value => {
    console.log('value2: ', value)
})

/*
输出结果(1.5秒后输出):
value1 : 成功的值
value2:  成功的值
*/

  就是说 .then 方法可以多次调用,每次调用都可以设置成功或失败的回调函数,则改写为如下:
Step3: 手写MyPromise-核心3 promise的then方法可以添加多个

// 手写MyPromise-核心3  promise的then方法可以添加多个

// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor){                   // new MyPromise() 接收一个执行器函数
        executor(this.resolve, this.reject)  // 执行器函数有两个参数:resolve和reject 是内部方法
    }

    status = PENDING                         // 标识该promise对象的状态
    value = undefined                        // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
    reason = undefined                       // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
    successCallback = []
    failCallback = []

    resolve = value => {                     // 用于把promise对象的状态更改为fulfilled-成功
        if(this.status !== PENDING) return;  // promise对象状态只能由pending-等待 变成 成功或失败
        this.status = FULFILLED
        this.value = value                   // 成功了的数据保存在this.value中 后续传递给成功的回调函数
        
        while(this.successCallback.length){  // 如果有保存而未执行的成功回调 则此时执行
            const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
            callback(this.value)
        }
    }
    reject = reason => {                     // 用于把promise对象的状态更改为rejected-失败
        if(this.status !== PENDING) return;
        this.status = REJECTED
        this.reason = reason                 // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
        // 如果有保存而未执行的失败回调 则此时执行
        while(this.failCallback.length){
            const callback = this.failCallback.shift()
            callback(this.reason)
        }
    }

    then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
        if(this.status == FULFILLED){
            successCallback(this.value)
        } else if(this.status == REJECTED){
            failCallback(this.reason)
        } else {                             // 调用then方法时,promise还没有得到终态
                                             // 要等到其得到终态后,再执行成功或失败的回调
                                             // 先把回调都存储起来
            this.successCallback.push(successCallback)
            this.failCallback.push(failCallback)
        }
    }
}

// 测试3
const promise2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(100)
    }, 2000)
})
promise2.then(val => {
    console.log('success value: ', val)
}, rea => {
    console.log('fail reason: ', rea)
})
promise2.then(val => {
    console.log('another then: ', val)
})

/*
输出结果(2秒后输出):- 符合预期
success value:  100
another then:  100
*/

promise对象的then方法,会返回一个新的promise对象,故而.then方法可以链式调用,后面then方法的回调函数拿到的参数的值 是上一个then方法的回调函数的 返回值:

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功的值')
    }, 1000)
})

// then方法可以被链式调用,后面then方法的回调函数拿到的参数的值是上一个then方法的回调函数的返回值
const r1 = promise.then(value => {
    console.log('value1 :', value)
    return '另一个值'
}).then(value => { // 此处拿到的参数value 是上一个then方法的回调函数的 返回值
    console.log('value2 :', value)
})
/*
输出结果(1秒后输出):
value1 : 成功的值
value2 : 另一个值
*/

Step4: 手写MyPromise-核心4 promise的then方法可以链式调用

// 手写MyPromise-核心4  promise的then方法可以链式调用
// 上一个then方法接收的回调函数的返回值 会传递给下一个then方法的回调函数作为参数
// then方法应该返回一个promise对象,这样才能链式调用

// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor){                   // new MyPromise() 接收一个执行器函数
        executor(this.resolve, this.reject)  // 执行器函数有两个参数:resolve和reject 是内部方法
    }

    status = PENDING                         // 标识该promise对象的状态
    value = undefined                        // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
    reason = undefined                       // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
    successCallback = []
    failCallback = []

    resolve = value => {                     // 用于把promise对象的状态更改为fulfilled-成功
        if(this.status !== PENDING) return;  // promise对象状态只能由pending-等待 变成 成功或失败
        this.status = FULFILLED
        this.value = value                   // 成功了的数据保存在this.value中 后续传递给成功的回调函数
        
        while(this.successCallback.length){  // 如果有保存而未执行的成功回调 则此时执行
            const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
            callback(this.value)
        }
    }
    reject = reason => {                     // 用于把promise对象的状态更改为rejected-失败
        if(this.status !== PENDING) return;
        this.status = REJECTED
        this.reason = reason                 // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
        // 如果有保存而未执行的失败回调 则此时执行
        while(this.failCallback.length){
            const callback = this.failCallback.shift()
            callback(this.reason)
        }
    }

    then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
        let promise2 = new MyPromise((resolve, reject) => { // 执行器内的代码立即执行

            if(this.status == FULFILLED){
                let x = successCallback(this.value)    // 上一个promise对象回调函数的返回值 需要传递给下一个promise对象的回调函数
                resolve(x)
            } else if(this.status == REJECTED){
                failCallback(this.reason)
            } else {                             // 调用then方法时,promise还没有得到终态
                                                 // 要等到其得到终态后,再执行成功或失败的回调
                                                 // 先把回调都存储起来
                this.successCallback.push(successCallback)
                this.failCallback.push(failCallback)
            }

        })
        return promise2
    }
}

// 测试3
const promise = new MyPromise((resolve, reject) => {
    resolve(100)
})

promise.then(val => {
    console.log('success value: ', val)
    return 500
}).then(val => {
    console.log('another then: ', val)
})

/*
输出结果(立即输出):- 符合预期
success value:  100
another then:  500
*/

在上述Step4代码中,then方法的链式调用逻辑里,仅处理了前一个then调用时已经得到成功终态的情况,没有处理前一个then调用时尚未得到终态的情况(比如前一个then的回调函数返回一个promise对象,则下一个then应该等待该promise得到终态,并把这个返回的promise对象的值传递给一下个then的回调函数)
Step5: 手写MyPromise-核心5  then的链式调用 处理前一个then调用若返回promise 而promise尚未得到终态的情况
 

// 手写MyPromise-核心5 处理前一个then调用时尚未得到终态的情况

// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor){                   // new MyPromise() 接收一个执行器函数
        executor(this.resolve, this.reject)  // 执行器函数有两个参数:resolve和reject 是内部方法
    }

    status = PENDING                         // 标识该promise对象的状态
    value = undefined                        // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
    reason = undefined                       // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
    successCallback = []
    failCallback = []

    resolve = value => {                     // 用于把promise对象的状态更改为fulfilled-成功
        if(this.status !== PENDING) return;  // promise对象状态只能由pending-等待 变成 成功或失败
        this.status = FULFILLED
        this.value = value                   // 成功了的数据保存在this.value中 后续传递给成功的回调函数
        
        while(this.successCallback.length){  // 如果有保存而未执行的成功回调 则此时执行
            const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
            callback()
        }
    }
    reject = reason => {                     // 用于把promise对象的状态更改为rejected-失败
        if(this.status !== PENDING) return;
        this.status = REJECTED
        this.reason = reason                 // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
        // 如果有保存而未执行的失败回调 则此时执行
        while(this.failCallback.length){
            const callback = this.failCallback.shift()
            callback()
        }
    }

    then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
        let promise2 = new MyPromise((resolve, reject) => { // 执行器内的代码立即执行

            if(this.status == FULFILLED){
                let x = successCallback(this.value)    // 上一个promise对象回调函数的返回值 需要传递给下一个promise对象的回调函数
                // 这里需要判断上一个promise对象回调函数的返回值x是普通值 还是另一个promise对象
                // 如果是普通值,则直接返回x
                // 如果是promise对象,则应等待该promise对象执行完毕(得到终态)
                //   该promise对象执行完毕的结果 如果是成功,则把成功的值传递给下一个then方法的成功回调
                //   如果是失败,则把失败的原因传递给一下一个then方法的失败回调
                handlePromiseReturned(x, resolve, reject)
            } else if(this.status == REJECTED){
                let x = failCallback(this.reason)
                handlePromiseReturned(x, resolve, reject)
            } else {                             // 调用then方法时,promise还没有得到终态
                                                 // 要等到其得到终态后,再执行成功或失败的回调
                                                 // 先把回调都存储起来
                this.successCallback.push(() => {
                    let x = successCallback(this.value)
                    handlePromiseReturned(x, resolve, reject)
                })
                this.failCallback.push(() => {
                    let x = failCallback(this.reason)
                    handlePromiseReturned(x, resolve, reject)
                })
            }

        })
        return promise2
    }
}

function handlePromiseReturned(x, resolve, reject){
    if(x instanceof MyPromise){
        x.then(value => {
            resolve(value) // then方法将要返回的promise对象的 resolve方法
        }, reason => {
            reject(reason) // then方法将要返回的promise对象的 reject方法
        })
    } else {
        resolve(x)
    }
}

// 测试3
const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('一个值')
    }, 1000)
})

const other = function() {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('另一个值')
        }, 2000)
    })
}

promise.then(val => {
    console.log('success value: ', val)
    return other() // other() 是一个promise对象 则下一个then将等待它得到终态后再执行
}).then(val => {
    console.log('another then: ', val)
})

/*
输出结果(1秒后输出第一行,再过2秒输出第二行):- 符合预期
success value:  一个值
another then:  另一个值
*/

3.实现功能全面的Promise
 在上面2.部分,我们按照如下思路,逐步实现了Promise的核心代码:

Step1: 手写MyPromise-核心 构造器接收一个执行器函数 promise对象内部状态 resolve/reject方法 then方法

Step2: 手写MyPromise-核心2 then方法调用时可能还未得到promise对象的终态

Step3: 手写MyPromise-核心3 promise的then方法可以添加多个(多次调用)

Step4: 手写MyPromise-核心4 promise的then方法可以链式调用

Step5: 手写MyPromise-核心5 then的链式调用 处理前一个then调用若返回promise 而该promise尚未得到终态的情况

下面,为实现一个功能全面的Promise,将为其继续添加如下Promise的特性:
禁止then方法的回调返回自身、

内部的错误捕获、

将then方法的参数变为可选参数(如果一个.then方法无回调函数传入,则其后面有回调函数的then方法来处理拿到的成功的值或失败的原因)、

Promise.all静态方法、

Promise.resolve静态方法、

promise对象的finally方法、

promise对象的catch方法。

Promise的finally方法示例:

// Promise的finally方法示例
function users(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(['Andy', 'Heiby'])
        }, 1000)
    })
}

users().then(value => {
    console.log('value: ', value)
    return '123'
}, reason => {
    console.log('reason: ', reason)
}).finally(() => { // finally接收一个最终回调方法 但finally方法拿不到promise最终的状态和值
    console.log('finally')
    return '456'
}).then(value => { // finally()后面的then()拿到的数据 与没有finally()时是一样的 
    console.log('another then, value: ', value)
})

/*
输出结果(1秒后输出):
value:  [ 'Andy', 'Heiby' ]
finally
another then, value:  123
*/


功能全面的MyPromise对象示例:
 

// 手写MyPromise-核心
/*
Step1: Promise是一个类 其构造器接收一个执行器函数 promise对象内部状态 resolve/reject方法 then方法
Step2: then方法调用时可能还未得到promise对象的终态
Step3: promise的then方法可以添加多个(多次调用)
Step4: promise的then方法可以链式调用
Step5: then的链式调用 处理前一个then调用若返回promise 而该promise尚未得到终态的情况
Step6: 禁止then方法的回调返回自身、
       内部的错误捕获、
       将then方法的参数变为可选参数(如果一个then方法无回调函数传入,
       则其后面有回调函数的then方法来处理拿到的成功的值或失败的原因)、
       Promise.all静态方法、
       Promise.resolve静态方法、
       promise对象的finally方法、
       promise对象的catch方法
*/

// promise对象的状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor){                   // new MyPromise() 接收一个执行器函数
        try{ // 内部的错误捕获
            executor(this.resolve, this.reject)  // 执行器函数有两个参数:resolve和reject 是内部方法
        } catch(e) {
            this.reject(e)
        }
        
    }

    status = PENDING                         // 标识该promise对象的状态
    value = undefined                        // resolve方法接收的参数:如果对象成功了,那么传入的数据是什么
    reason = undefined                       // reject方法接收的参数:如果对象失败了,那么失败的原因是什么
    successCallback = []
    failCallback = []

    resolve = value => {                     // 用于把promise对象的状态更改为fulfilled-成功
        if(this.status !== PENDING) return;  // promise对象状态只能由pending-等待 变成 成功或失败
        this.status = FULFILLED
        this.value = value                   // 成功了的数据保存在this.value中 后续传递给成功的回调函数
        
        while(this.successCallback.length){  // 如果有保存而未执行的成功回调 则此时执行
            const callback = this.successCallback.shift() // 回调函数数组的第一个先执行
            callback()
        }
    }
    reject = reason => {                     // 用于把promise对象的状态更改为rejected-失败
        if(this.status !== PENDING) return;
        this.status = REJECTED
        this.reason = reason                 // 失败了的原因保存在this.reason中 后续传递给失败的回调函数
        // 如果有保存而未执行的失败回调 则此时执行
        while(this.failCallback.length){
            const callback = this.failCallback.shift()
            callback()
        }
    }

    then = (successCallback, failCallback) => { // promise对象得到终态后的回调是什么
        successCallback = successCallback ? successCallback : value => value     // 将then方法的参数变为可选参数
        failCallback = failCallback ? failCallback : reason => { throw reason }  // 将then方法的参数变为可选参数

        let promise2 = new MyPromise((resolve, reject) => { // 执行器内的代码立即执行

            if(this.status == FULFILLED){
                setTimeout(() => {
                    try {  // 内部的错误捕获
                        let x = successCallback(this.value)    // 上一个promise对象回调函数的返回值 需要传递给下一个promise对象的回调函数
                        // 这里需要判断上一个promise对象回调函数的返回值x是普通值 还是另一个promise对象
                        // 如果是普通值,则直接返回x
                        // 如果是promise对象,则应等待该promise对象执行完毕(得到终态)
                        //   该promise对象执行完毕的结果 如果是成功,则把成功的值传递给下一个then方法的成功回调
                        //   如果是失败,则把失败的原因传递给一下一个then方法的失败回调
                        handlePromiseReturned(promise2, x, resolve, reject)
                    } catch(e) {
                        reject(e)
                    }
                }, 0)
            } else if(this.status == REJECTED){
                setTimeout(() => {
                    try{ // 内部的错误捕获
                        let x = failCallback(this.reason)
                        handlePromiseReturned(x, resolve, reject)
                    } catch(e){
                        reject(e)
                    }
                }, 0)
            } else {                             // 调用then方法时,promise还没有得到终态
                                                 // 要等到其得到终态后,再执行成功或失败的回调
                                                 // 先把回调都存储起来
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try{ // 内部的错误捕获
                            let x = successCallback(this.value)
                            handlePromiseReturned(promise2, x, resolve, reject)
                        } catch(e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try{ // 内部的错误捕获
                            let x = failCallback(this.reason)
                            handlePromiseReturned(promise2, x, resolve, reject)
                        } catch(e){
                            reject(e)
                        }
                    }, 0)
                })
            }

        })
        return promise2
    }

    finally = (callback) => { // finally接收一个回调函数 在promise得到终态后执行
                              // finally后面仍可以链式调用then() 后面的then()会等待finally回调函数执行完毕(得到终态)
        this.then(value => {
            // callback()
            return MyPromise.resolve(callback()) // MyPromise.resolve把一个值转换成promise
                    .then(() => value)           // 把promise成功的值返回
        }, reason => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }
    
    catch = failCallback => {
        return this.then(undefined, failCallback)
    }

    static all = (array) => { // Promise.all方法的返回值是一个promise对象
        return new MyPromise((resolve, reject) => {
            let results = []
            let count = 0
            function addResult(key, result){
                results[key] = result
                count++
                if(count == array.length){
                    resolve(results)
                }
            }
            for(let i = 0; i < array.length; i++){
                const current = array[i]
                if(current instanceof MyPromise){
                    current.then(value => {
                        addResult(i, value)
                    }, reason => {
                        reject(reason) // array中的任何一个promise失败,则Promise.all返回的promise就是状态失败
                    })
                } else {
                    addResult(i, current)
                }
            }
        })
    }

    static resolve = value => { // Promise.resolve方法把传入的值包装成promise对象 如果参数就是promise对象,则直接返回
        if(value instanceof MyPromise) return value
        return new MyPromise((resolve, reject) => {
            resolve(value)
        })
    }
}

function handlePromiseReturned(promise2, x, resolve, reject){
    if(x instanceof MyPromise){

        if(promise2 === x) return reject(new Error('禁止then方法的回调返回自身')); // 禁止then方法的回调返回自身

        x.then(value => {
            resolve(value) // then方法将要返回的promise对象的 resolve方法
        }, reason => {
            reject(reason) // then方法将要返回的promise对象的 reject方法
        })
    } else {
        resolve(x)
    }
}

/* 测试 */
function users(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(['Andy', 'Heiby'])
        }, 1000)
    })
}

var promise = new MyPromise((resolve, reject) => {
    resolve('成功的值')
})

// then()方法参数可选
/* 
const r1 = promise.then(console.log, console.log)
const r2 = promise.then().then().then(console.log, console.log)
*/
/*
输出结果(立即输出):
成功的值
成功的值
 */


// Promise.all静态方法
/* 
function p1(){
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('p1')
        }, 2000)
    })
}
function p2(){
    return new MyPromise((resolve, reject) => {
        resolve('p2')
    })
}
var promiseAll = MyPromise.all(['a', 'b', p1(), p2(), 'c'])
    .then(values => {
        console.log(values)
    }, reason => {
        console.log('error: ', reason)
    })
*/
/*
输出结果(2秒后输出):
[ 'a', 'b', 'p1', 'p2', 'c' ]
 */

本文 完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值