从零开始的Promise(一)

208 阅读4分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

本章主要带大家学习Promise的相关知识点,希望能通过本章来彻底掌握js世界中Promise相关知识

高阶函数

一个函数可以接受一个参数是函数,或者一个函数返回一个函数,我门称这个函数为高阶函数。 高阶函数的作用有哪些呢?

  1. 扩展函数的功能
function say(args) { // 我们需要对say方法进行扩展,但是不能修改源代码
    console.log('say', args)
}
Function.prototype.before = function (cb) {
    return (...args) => { // newSay
        cb();
        this(...args); // 扩展原来的函数
    }
}
let newSay = say.before(() => {
    console.log('beforeSay')
})
newSay('hello');
  1. 保留参数变量
function isType(typing) {
    return (val) => {
        return Object.prototype.toString.call(val) === `[object ${typing}]`
    }
}
// 利用高阶函数保留参数变量
let isString = isType('String'); 
console.log(isString('hello'));// true
console.log(isString(123)); // false

异步处理

前端常见的就是 同时发送多个请求,最终拿到多个请求的返回值 来进行渲染页面,下面会介绍几种方法 这里创建两个文件用来模拟异步的请求数据name.txt, age.txt;内容分别为小明18

回调函数

通过回调函数来处理异步加载操作,当所有的异步请求都完成后打印data

const fs = require('fs')
const path = require('path')
const fn = function(timer, callback){
    const obj = {}
    return (k,v)=>{
        obj[k] = v
        if (--timer === 0){ // 这里保留了参数
            callback(obj)
        }
    }
}
const done = fn(2, (data) => {
    console.log('data',data) // {name: '小明',age: '18'}
})
fs.readFile(path.resolve(__dirname, 'name.txt'), 'utf8', (err, data)=>{
    done('name', data)
})
fs.readFile(path.resolve(__dirname, 'age.txt'), 'utf8', (err, data)=>{
    done('age', data)
})

发布订阅模式

上面的代码有点杂乱,不是很易读,下面利用发布订阅模式来处理

const fs = require('fs')
const path = require('path')
// 发布订阅模式
let  event = {
    _events: [], // 存放事件,发布的时候执行
    on(fn){
        this._events.push(fn) // 订阅
    },
    emit(...age){
        this._events.forEach(fn => {
            fn(...age)
        })
    }
}
let obj = {}
event.on((k,v)=>{ // 订阅
    obj[k] = v
    if (Object.keys(obj).length === 2){
        console.log('---', obj)
    }
})

fs.readFile(path.resolve(__dirname, 'name.txt'), 'utf8', (err, data)=>{
    event.emit('name', data) // 读取完数据发布消息
})
fs.readFile(path.resolve(__dirname, 'age.txt'), 'utf8', (err, data)=>{
    event.emit('age', data) // 读取完数据发布消息
})

当读完一个文件后,发布消息,执行赋值操作,这样看起来清晰很多。

观察者模式

观察者模式包含发布订阅模式,不同点在于发布订阅模式中发布和订阅是没有耦合关系的,发布时间取决于自己,而在观察者模式中被观察者需要收集观察者,当被观察者状态改变时来主动通知观察者。

class Subject {
    constructor(name){
        this.name = name
        this.event = []
        this.status = ''
    }
    changeStatus(val){
        this.status = val
        this.event.forEach(item => {
            item.updata(this) // 主动通知更新
        })
    }
    add(o){
        this.event.push(o)
    }
}
class Observer{
    constructor(name){
        this.name = name
    }
    updata(bb){
        console.log(this.name,':观察到小宝宝', bb.name, bb.status)

    }
}
let ff = new Observer('ff')
let mm = new Observer('mm')

let bb = new Subject('bb')
bb.add(f) // 被观察者收集观察者
bb.add(mm) // 被观察者收集观察者
setTimeout(() => {
    bb.changeStatus('哭')
    // ff :观察到小宝宝 bb 哭
    // mm :观察到小宝宝 bb 哭
}, 3000);

Promise-简单的同步版

在有上面的基础下,可以实现一个简单的同步版promise;首先了解promise有哪些东西。

  1. Promise是一个类,通过new来创建,实例上有一个then方法
  2. promise有三种不可转换状态,PENDING REJECTED FULFILLED
  3. promise传入一个立即执行函数executor
  4. promise有一个成功的数据value和一个失败原因reason
  5. promise中出现异常会执行失败的逻辑 下面根据上面的条件生成一个自己的Promise
const PENDING = 'PENDING'; // 三种状态
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
    constructor(executor){
        this.value = undefined // 成功原因
        this.reason = undefined // 失败原因
        this.state = PENDING // 状态
        const resovle = (value)=>{ // 改变状态方法
            this.value = value // 存储成功值
            this.state = FULFILLED // 改变状态
        }
        const reject = (value)=>{
            this.reason = value // 存储失败原因
            this.state = REJECTED // 改变状态
        }
        try{ // 处理异常,若异常走失败处理逻辑
            executor(resovle, reject) // 立即执行函数执行
        }catch(err){
            reject(err)
        }
    }
    then(suc, rej){
        if (this.state === FULFILLED){ // 判断状态-成功,执行suc函数并把成功值传递
            suc(this.value)
        }
        if (this.state === REJECTED){ // 判断状态-失败,执行rej函数并把失败原因传递
            rej(this.reason)
        }
    }
}
let a = new Promise((resovle, rej)=>{
    console.log('---')
    // throw new Error('0000')
    // resovle('成功')
    rej('失败')
})
a.then((res)=>{
    console.log('res', res) // resovle('成功') : ‘成功’
},(rej)=>{
   console.log(' rej', rej) // ‘失败’ 
   // throw new Error('0000') : '00000'
})

这样就实现了一个最简单的promise,现在实现的promise可以.then,可以判断成功失败进行不同处理,但是不能进行异步的处理,因为这个promise中没有处理异步逻辑,在then中会一直认为是等待状态,下一篇会处理异步,链式调用等问题。