JavaScript深入理解(18课)笔记-响应式模拟和Promise缘由解析

18.npm、yarn、cnpm包管理工作


响应式-正确收集依赖的过程

// 监听对象的属性变量:Proxy(vue3)/0bject.defineProperty(vue2)
const objProxy = new Proxy(obj, {
    get: function(target, key, receiver) {
        // 根据target.key获取对应的depend
        const depend = getDepend(target, key)
        // 给depend对象中添加响应函数
        depend.addDepend(activeReactiveFn)
        return Reflect.get(target, key, receiver)
    },
    set: function(target, key, newValue, receiver) {
        Reflect.set(target, key, newValue, receiver)
        // depend.notify()
        const depend = getDepend(target, key)
        depend.notify()
    }
})


【对Depend类重构】

00:44:00

01:00:00
响应式函数模拟Vue3示例代码:
// 保存当前需要收集的响应式函数
let activeReactiveFn = null

/**
 * Depend优化:
 *    1> depend方法
 *    2> 使用Set来保存依赖函数, 而不是数组[]
 */

class Depend {
    constructor() {
        this.reactiveFns = new Set()
    }

    // addDepend(reactiveFn) {
    //     this.reactiveFns.add(reactiveFn)
    // }

    depend() {
        if (activeReactiveFn) {
            this.reactiveFns.add(activeReactiveFn)
        }
    }

    notify() {
        this.reactiveFns.forEach(fn => {
            fn()
        })
    }
}

// 封装一个响应式的函数
function watchFn(fn) {
    activeReactiveFn = fn
    fn()
    activeReactiveFn = null
}

// 封装一个获取depend函数
const targetMap = new WeakMap()
function getDepend(target, key) {
    // 根据target对象获取map的过程
    let map = targetMap.get(target)
    if (!map) {
        map = new Map()
        targetMap.set(target, map)
    }

    // 根据key获取depend对象
    let depend = map.get(key)
    if (!depend) {
        depend = new Depend()
        map.set(key, depend)
    }
    return depend
}

function reactive(obj) {
    return new Proxy(obj, {
        get: function(target, key, receiver) {
            // 根据target.key获取对应的depend
            const depend = getDepend(target, key)
            // 给depend对象中添加响应函数
            // depend.addDepend(activeReactiveFn)
            depend.depend()
    
            return Reflect.get(target, key, receiver)
        },
        set: function(target, key, newValue, receiver) {
            Reflect.set(target, key, newValue, receiver)
            // depend.notify()
            const depend = getDepend(target, key)
            depend.notify()
        }
    })
}

// 监听对象的属性变量: Proxy(vue3)/Object.defineProperty(vue2)
const objProxy = reactive({
    name: "why", // depend对象
    age: 18 // depend对象
})

const infoProxy = reactive({
    address: "广州市",
    height: 1.88
})

watchFn(() => {
    console.log(infoProxy.address)
})

infoProxy.address = "北京市"

const foo = reactive({
    name: "foo"
})

watchFn(() => {
    console.log(foo.name)
})

foo.name = "bar"


控制台输出:
广州市
北京市
foo
bar

01:11:00
Promise使用详解
【任何一个新技术的出现,都是为了解决原有技术的某一个痛点而产生的。】

异步任务的处理

在ES6出来之后,有很多关于Promise的讲解、文章,也有很多经典的书籍讲解Promise
· 虽然等你学会Promise之后,会觉得Promise不过如此,但是在初次接触的时候都会觉得这个东西不好理解;
那么这里我从一个实际的例子来作为切入点:
· 我们调用一个函数,这个函数中发送网络请求(我们可以用定时器来模拟);
· 如果发送网络请求成功了,那么告知调用者发送成功,并且将相关数据返回过去;
· 如果发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息;

示例讲解“承诺Promise”缘由:
/**
 * 这种回调的方式有很多的弊端:
 *    1> 如果是我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称, 并且使用好
 *    2> 如果我们使用的是别人封装的requestData或者一些第三方库, 那么我们必须去看别人的源码或者文档, 才知道它这个函数需要怎么去获取到结果
 */

// request.js
function requestData(url, successCallback, failtureCallback) {
    // 模拟网络请求
    setTimeout(() => {
        // 拿到请求的结果
        // url传入的是coderwhy, 请求成功
        if (url === "coderwhy") {
            // 成功
            let names = ["abc", "cba", "nba"]
            successCallback(names)
        } else { // 否则请求失败
            // 失败
            let errMessage = "请求失败, url错误"
            failtureCallback(errMessage)
        }
    }, 3000);
}

// main.js
requestData("kobe", (res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})

// 更规范/更好的方案 Promise承诺(规范好了所有的代码编写逻辑)
function requestData2() {
    return "承诺"
}

const chengnuo = requestData2()

01:41:00

什么是Promise呢?
在上面的解决方案中,我们确确实实可以解决请求函数得到结果之后,获取到对应的回调,但是它存在两个主要的
问题:
· 第一,我们需要自己来设计回调函数、回调函数的名称、回调函数的使用等;
· 第二,对于不同的人、不同的框架设计出来的方案是不同的,那么我们必须耐心去看别人的源码或者文档,以便可以理解它这个函数到底怎么用;

我们来看一下Promise的API是怎么样的:
Promise是一个类,可以翻译成承诺、许诺、期约;
· 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;
· 在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor
    √这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject ;
    √当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
    √当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;


function Person(name, age) { }
const p = new Person("why",18)

//类似上面的,new创建Promise返回的是一个“promise承诺”,也是可以传入参数的(参数是:回调函数,并且传入进去就会立即执行)
const promise = new Promise(() =>{ })

//上面的Promise形式也可以用 class 类来类似说明:
class Person {
    constructor(callback) {
        callback()
    }
}
调用时类似:
const p = new Person(() =>{ })

上面中“传入参数【回调函数】”传入的这个函数,被称之为executor,executor在执行的时候会传入一些其他的参数。继续举例:
class Person {
    constructor(callback) {
        let foo = function() { }
        let bar = function() { }

        callback(foo, bar)
    }
}
const p = new Person((foo, bar) =>{ 
    foo()
    bar()
})

对应Promise调用类似【其中:resolve 和 reject 也都是回调函数】:
const promise = new Promise((resolve, reject) =>{ 
    resolve()
})

promise.then(() =>{ })

规定:
在成功时,回调resolve函数;
在失败时,回调reject函数。

在拿到 promise 后,就能执行:
promise.then(() =>{ })

即:在你调用 resolve() 之后,外面的 then方法中的回调函数就会自动执行!

同样地,
即:在你调用 reject() 之后,外面的 catch方法中的回调函数就会自动执行!
promise.catch(() =>{ })

上面就是Promise最基本的创建过程。

这样,有利于在设计API时,只要知道你这个函数返回的是Promise,就可以在拿到结果后,直接写个then(…) 跟 catch(…) 。
知道,成功之后肯定执行then,失败肯定执行catch

还一种写法,直接在then(…) 中传入两个回调函数,第一个是成功的,第二个是失败的。


01:58:00
结合Promise,现在改一下上面的异步函数示例:
function requestData(……) 改成用 Promise 来写。
function requestData(url,) {
    // 异步请求的代码会被放入到executor中
    return new Promise((resolve, reject) => {
        // 模拟网络请求
        setTimeout(() => {
            // 拿到请求的结果
            // url传入的是coderwhy, 请求成功
            if (url === "coderwhy") {
                // 成功
                let names = ["abc", "cba", "nba"]
                resolve(names)
            } else { // 否则请求失败
                // 失败
                let errMessage = "请求失败, url错误"
                reject(errMessage)
            }
        }, 3000);
    })
}

// main.js
const promise = requestData("coderwhy")
promise.then((res) => {
    console.log("请求成功:", res)
}, (err) => {
     console.log("请求失败:", err)
})


Promise就是为了解决类似于异步处理函数的各种方案,它不希望我们自己来设计、它给我们一种比较固定、规范的方法;
在两个文件或函数之间沟通的时候,返回一个Promise,我们在then中传入回调。

示例:

function foo() {
    // Promise
    return new Promise((resolve, reject) => {
        resolve("success message")
        // reject("failture message")
    })
}

// main.js
const fooPromise = foo()
// then方法传入的回调函数两个回调函数:
// > 第一个回调函数, 会在Promise执行resolve函数时, 被回调
// > 第二个回调函数, 会在Promise执行reject函数时, 被回调
fooPromise.then((res) => {
    console.log(res)
}, (err) => {
    console.log(err)
})


【回调函数,也叫Hook函数(勾子函数)】


// 完全等价于下面的代码
// 【注意】: Promise状态一旦确定下来, 那么就是不可更改的(锁定)
new Promise((resolve, reject) => {
    // pending状态: 待定/悬而未决的
    console.log("--------")
    reject() // 处于rejected状态(已拒绝状态)
    resolve() // 处于fulfilled状态(已敲定/兑现状态)
    console.log("++++++++++++")
}).then(res => {
    console.log("res:", res)
}, err => {
    console.log("err:", err)
})

输出:
--------
++++++++++++
err: undefined


Promise划分状态表现图


上面Promise使用过程,我们可以将它划分成三个状态:
待定(pending):初始状态,既没有被兑现,也没有被拒绝;
    √当执行executor中的代码时,处于该状态;
已兑现(fulfilled或resolved):意味着操作成功完成;
    √执行了resolve时,处于该状态;
已拒绝(rejected):意味着操作失败;
    √执行了reject时,处于该状态;

/**
 * resolve(参数)
 *  1> 普通的值或者对象  pending -> fulfilled
 *  2> 传入一个Promise
 *    那么当前的Promise的状态会由传入的Promise来决定
 *    相当于状态进行了移交
 *  3> 传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口)
 *    那么也会执行该then方法, 并且又该then方法决定后续状态
 */

// 示例1.传入Promise的特殊情况
const newPromise = new Promise((resolve, reject) => {
    // resolve("aaaaaa")
    reject("err message")
})

new Promise((resolve, reject) => {
    // pending -> fulfilled
    resolve(newPromise)
}).then(res => {
    console.log("res:", res)
}, err => {
    console.log("err:", err)
})


输出:
err: err message

// 示例2.传入一个对象, 这个兑现有then方法
const newPromise = new Promise((resolve, reject) => {
    // resolve("aaaaaa")
    reject("err message")
})
new Promise((resolve, reject) => {
    // pending -> fulfilled
    const obj = {
        then: function(resolve, reject) {
            // resolve("resolve message")
            reject("reject message")
        }
    }
    resolve(obj)
}).then(res => {
    console.log("res:", res)
}, err => {
    console.log("err:", err)
})

输出:
err: reject message
【报错】 Uncaught (in promise) err message


// 示例3:针对示例2报错,需要相应加上catch
const newPromise = new Promise((resolve, reject) => {
    // resolve("aaaaaa")
    reject("err message")
}).catch(err => {
    console.log("era:", err)
})
new Promise((resolve, reject) => {
    // pending -> fulfilled
    const obj = {
        then: function(resolve, reject) {
            // resolve("resolve message")
            reject("reject message")
        }
    }
    resolve(obj)
}).then(res => {
    console.log("res:", res)
}, err => {
    console.log("err:", err)
})

输出:
era: err message
err: reject message


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值