javascript --- > 手写Promise、快排、冒泡、单例模式+观察者模式

手写promise

一种异步的解决方案, 参考

Promise代码基本结构

function Promise(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    
    function resolve(){
        
    }
    
    function reject(){
        
    }
}
module.exports = Promise

state保存的是当前的状态,在Promise状态发展只有以下两种模式且不可逆:

pending - 等待态
resolved - 成功态
rejected - 失败态

从上可知,状态只能由pending变为resolved(执行resolve)或pending变为rejected(执行reject)

这就引出了resolve和reject的功能

function Promise(executor){
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    
    function resolve(value){
        this.state = 'resolved'
        this.value = value
    }
    
    function reject(reason){
        this.state = 'rejected'
        this.reason = reason
    }
    
    executor(resove, reject)
}

then方法的实现

当Promise的状态发生了改变,不论是成功或是失败都会调用then方法

let p = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve(1)
    },50)
})
p.then(data=>{console.log(data)})

可以得出then的方法,由于then方法是每个实例所以共有,因此可以将其写在原型链上:

  • 接受2个参数,成功的回调onFulfilledonRejected
    • 两个函数分别在resolved状态下和rejected状态下执行
Promise.prototype.then = function(onFulfilled, onRejected){
    if(this.state === 'resolved'){
        onFulfilled(this.value)
    }
    if(this.state === 'rejected'){
        onRejected(this.reason)
    }
}

当你写出了以上的代码,在同步的情况下执行完全没有问题. 但是在异步的情况下,流程如下:

  • 函数执行new Promise,当遇到setTimeout时,会将其推入一个异步队列中
  • 然后执行p.then: 浏览器会顺着原型链找到Promise.prototype.then发现此时的状态是pending,故不做任何处理,结束同步处理
  • 开始执行异步队列里面的setTimeout,执行resovle(1).

异步方法的实现

处理异步方法的关键是,在Promise.prototype.then方法中,当状态为pending时,可以将处理函数作为变量存储起来,这样当异步过后,执行resolve时,可以在内存中找到相应的处理函数并对其进行执行.

这就需要我们修改Promise构造函数

function Promise(exector){
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onResolvedCallbacks = []	// 用于存储成功的回调
    this.onRejectedCallbacks = []	// 用于存储失败的回调
    
    function resolve(value){
        if(this.state === 'pending'){
            this.state = 'resolved'
            this.value = value
            this.onResolvedCallbacks.forEach(resolved=> resolved(value))
        }
    }
    function reject(reason){
        if(this.state === 'pending'){
            this.state = 'rejected'
            this.reason = reason
            this.onRejectedCallbacks.forEach(rejected=> rejected(reason))
        }
    }
}

// 修改 Promise.prototype.then
Promise.prototype.then = function (onFulfilled, onRejected){
    if(this.state === 'resolved'){
        onFulFilled(this.value)
    }
    if(this.state === 'rejected'){
        onRejected(this.reason)
    }
    // 异步: 将回调函数放入内存中.
    if(this.state === 'pending'){ 
        if(typeof onFulfilled === 'function'){
            this.onResolvedCallbacks.push(onFulfilled)
        }
        if(typeof onRejected  === 'function'){
            this.onRejectedCallbacks.push(onRejected)
        }
    }
}

当涉及到异步,使用this时要绝对小心

在使用setTimeout,里面回调使用的函数中的this是指向window的(通过debugger调试可以获得).这就需要在一开始时,将指向实例的this保存下来.

function Promise(exector){
    let _this = this
    _this.state = 'pending'
    _this.value = undefined
    _this.reason = undefined
    _this.onResovledCallbacks = []
    _this.onRejectedCallbacks = []
    
    function resolve(value){
        _this.state = 'resolved'
        _this.value = value
        _this.onResolvedCallbacks.forEach(resolved => resolved(value))
    }
    function reject(reason){
        _this.state = 'rejected'
        _this.reason = reason
        _this.onRejectedCallbacks.forEach(rejected=> rejected(reason))
    }
    exector(resolve, reject)
}
Promise.prototype.then = function(onFulfilled, onRejected){
    if(this.state === 'resolved'){
        onFulfilled(this.value)
    }
    if(this.state === 'rejected'){
        onRejected(this.reason)
    }
    if(this.state === 'pending'){
        if(typeof onFulfilled === 'function'){
            this.onResolvedCallbacks.push(onFulfilled)
        }
        if(typeof onRejected === 'function'){
            this.onRejectedCallbacks.push(onRejected)
        }
    }
}

快排

思路: 将每次的第一个元素作为基准,将比基准小的放在left数组中,比基准大的放在right数组中.然后返回[left, pivot,right]

function qSort(arr){
    let pivot,left = [],right = []
    function loop(arr){
        pivot = arr[0]
        left = []
        right = []
        for(let i=0, len = arr.length; i < len; i++){
            if(arr[i] > pivot){
                right.push(arr[i])
            } else{
                left.push(arr[i])
            }
        }
        return loop(left).concat(pivot, loop(right))
    }
    return loop(arr)
}

冒泡排序

思路:

  • 记录待排序的数组长度 len
  • 当len大于0时,会进入循环
    • 会使用nextLen保存下次循环的长度.(为undefined会跳出循环)
    • 每次循环都从第0号位开始,最后到len.
    • 比较左右2边的元素
      • 左 > 右: 则交换左右两边的位置,并将nextLen置为当前的下标
    • 每次到循环结束,都会将len设为nextLen
function bubbleSort(arr){
    let len = arr.length;
    while(len > 0){
        let tmp,nextLen
        for(let i =0 ; i< len; i++){
            if(arr[i]>arr[i+1]){
                tmp = arr[i]
                arr[i] = arr[i+1]
                arr[i+1] = tmp
                nextLen = i
            }
            len = nextLen
        }
    }
    return arr
}

单例模式实现 发布/订阅 模式

  • 单例模式即只有一个实例

  • 发布订阅模式,即使用addDep添加依赖.使用notify

class Observer{
	constructor(){
        this.events ={}
    }
    
	addDep(tag, handler){
        if(Array.isArray(this.events[tag])){
            // 是一个数组
            this.events[tag].push(handler)
        }else{
            this.events[tag] = [handler]
        }
    }
    
    notify(tag, params){
        this.events[tag].forEach(fn => fn(params))
    }
}

以上实现了一个简单的观察者模式,下面使用单例模式对其进行改造.

简单的说就是在使用new Observer时,返回的是同一个实例.给构造函数添加一个获取单例的方法

class Observer{
    constructor(){
        this.events = {}
        this.instance = null
    }
}
Observetr.getInstance = function (){
    if(this.instance == null){
        this.instance = new Observer
    }
    return this.instance
}

let o1 = Observetr.getInstance()
let o2 = Observetr.getInstance()
console.log(o1 === o2)		// true

发布了228 篇原创文章 · 获赞 41 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/piano9425/article/details/104703601