zoukankan      html  css  js  c++  java
  • 手写Promise

    Promise

    高阶函数:
    一个函数的参数是一个函数 (回调)
    一个函数返回一个函数 (拆分函数)

    AOP 面向切片 装饰

    把核心抽离出来 在核心基础上增加功能

    // 1. 返回一个函数
    // 2. ** 函数中先调用传入的增加函数再调用主函数
    Function.prototype.before = function(beforeFn){
        return (...args)=>{ // 箭头函数中没有this指向 没有arguments 所以会像上级作用域查找
            beforeFn();
            this(...args); // this指say函数
        }
    }
    
    const say = (...args)=>{
        console.log('说话',args);
    }
    const newSay = say.before(()=>{
        console.log('您好')
    })
    const newSay1 = say.before(()=>{
        console.log('天气很好')
    })
    newSay(1,2,3);  //newSay指返回的函数
    newSay1();
    
    1. 箭头函数中没有this指向 没有arguments 都会向上级作用域查找

    柯里化

    把一个大函数拆分成很多具体功能的函数

    //使add封装为可以分批传入任意个数参数的方式调用
    const add = (a, b, c, d, e) => {
       return a + b + c + d + e;
    };
    
    // ** curring功能--收集参数,判断如果传参不够则返回自身函数下次调用收集,如果传参够了则调用目标函数
    const curring = (fn,arr = [])=>{ 
        let len = fn.length; 
        return (...args)=>{
            arr = arr.concat(args);  //收集参数
            if(arr.length < len){  //参数不够递归收集
                return curring(fn,arr);
            }
            return fn(...arr); //参数够了调用目标函数
        }
    }
    let r = curring(add)(1)(2)(3)(4); // [1,2,3,4,5]
    
    1. fn.length : 函数的形参个数,无法得知实参个数。

    发布订阅模式

    订阅时将函数放入数组,发布时遍历数组并调用函数

    on(订阅):将订阅事件存入数组中,
    emit(发布):遍历调用订阅的(数组中)事件。

    const fs = require('fs');
    
    let e = { // events模块   vue $on $once $off
        arr:[],
        on(fn){
            this.arr.push(fn); // redux
        },
        emit(){
            this.arr.forEach(fn => fn());
        }
    }
    e.on(()=>{ // 订阅
        console.log('ok')
    })
    e.on(()=>{ // 订阅
        if(Object.keys(school).length === 2){
            console.log(school)
        }
    })
    
    let school = {};
    fs.readFile('name.txt','utf8',(err,data)=>{
        school['name'] = data;
        e.emit(); // 发布
    }); 
    fs.readFile('age.txt','utf8',(err,data)=>{
        school['age'] = data;
        e.emit(); // 发布
    }); 
    

    观察者模式

    被观察者将观察者(多个实例)存入数组中,被观察者状态发生变化时遍历数组通知观察者(调用观察者方法)。

    class Subject { // 被观察者 小宝宝
        constructor(){
            this.arr = []; // [o1,o2]
            this.state = '我很开心'
        }
        attach(o){ // 原型上的方法
            this.arr.push(o);
        }
        setState(newState){
            this.state = newState;
            this.arr.forEach(o=>o.update(newState))
        }
    }
    //观察者模式包含发布订阅
    class Observer{ // 观察者 我 我媳妇
        constructor(name){
            this.name = name
        }
        update(newState){
            console.log(this.name + '小宝宝:'+newState)
        }
    }
    let s = new Subject('小宝宝'); // 小宝宝
    let o1 = new Observer('我');
    let o2 = new Observer('我媳妇')
    s.attach(o1);
    s.attach(o2);
    s.setState('不开心了');
    

    promise

    const p = new Promise((resolve, reject) => {
      // reject('hello');
      // throw Error();
      setTimeout(() => {
        resolve('hello')
      })
    })
    
    p.then((data) => {
      console.log(data)
    }, (err) => {
      console.log(err)  
    })
    

    通过resolve()reject()来改变promise内部状态,从而决定调用then里面的成功还是失败函数。

    链式调用

    1. 想走下一个then的失败函数
      1. 抛出一个异常
      2. 返回一个失败的promise
        .
    2. 如果then返回一个普通值 会走下一个then的成功,
    3. 如果then返回promise 就让promise 执行,并采用他的状态判断走下一个then的成功或失败
    4. 下一个then走向只由上一个then决定,下一个then的data是上一个then的返回值
     function readFile(...args){
        return new Promise((resolve,reject)=>{
            fs.readFile(...args,function(err,data){
                if(err)reject(err);
                resolve(data);
            })
        })
    }
    readFile('./name.txt','utf8').then(data=>{
        return readFile(data,'utf8')
    },err=>{
    }).then(data=>{
        console.log(data);
    },err=>{
        console.log(err);
    })
    

    promise简易源码实现

    const Promise = require('./promise.js');
    const p = new Promise((resolve, reject) => {
    
      // **同步时先走resove方法,异步时先走then方法
      setTimeout(() => {
        resolve('hello')
      });
      // reject('hello');
      // throw Error();
      
    })
    // 多次调用
    p.then((data) => {
      console.log(data)
    }, (err) => {
      console.log(err)  
    })
    p.then((data) => {
      console.log(data)
    }, (err) => {
      console.log(err)  
    })
    
    // 链式调用
    p.then((data) => {
      console.log(data)
    }, (err) => {
      console.log(err)  
    })
    .then((data) => {
      console.log(data)
    }, (err) => {
      console.log(err)  
    })
    /*
    * 1. 第一个then只有抛出错误或反返回失败的paomise,第二个then才会走失败
    */ 
    
    const PENDING = "PENDING";
    const FULFILLED = "FULFILLED";
    const REJECTED = "REJECTED";
    
    class Promise{
      constructor(executor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
          if (this.state === PENDING) {
            this.state = FULFILLED;
            this.value = value;
            // 发布 异步时then的订阅
            this.onResolvedCallbacks.forEach(fn => fn())
          }
        }
        const reject = (reason) => {
          if (this.state === PENDING) {
            this.state = REJECTED;
            this.reason = reason;
            this.onRejectedCallbacks.forEach(fn => fn())
          }
        }
        try {
          executor(resolve, reject)
        } catch (err) {
          reject(this.reason)
        }
      }
      then(onFulfilled, onRejected) {
        if (this.state === FULFILLED) {
          onFulfilled(this.value);
        }
        if (this.state === REJECTED) {
          onRejected(this.reason);
        }
        if (this.state === PENDING) {
            //订阅 异步时then里的回调
          this.onResolvedCallbacks.push(() => {
            onFulfilled(this.value);
          });
          this.onRejectedCallbacks.push(() => {
            onRejected(this.reason);
          });
        }
    
      }
    }
    module.exports = Promise;
    

    promise源码

    不包含Promise静态方法

    const PENDING = "PENDING";
    const FULFILLED = "FULFILLED";
    const REJECTED = "REJECTED";
    const resolvePromise = (promise2, x, resolve, reject) => {
      /**
       * 如果x为promise,判断x(回调返回的promise)是否为内部默认返回的promise2。
       * 如果是,就会出现死循环。
       * 自己等待自己完成(promise2需要等待then的回调返回的promise状态)。
       * 此时直接返回promise2的reject,并传入错误
       */
      if (promise2 === x) {
        return reject(new TypeError(`Chaining cycle detected for promise #<Promise>`));
      }
    
      /** 
       * 判断x是否为promise
       */
      if ((typeof x === 'object' && x !== null) || typeof x === 'function') { 
        /**
         * 防止骚屌丝返回自己写的promise调过成功再调失败
         */
        let called;
    
        try { // x.then有可能报错
          let then = x.then;
          if (typeof then === 'function') {  /** 就认为是promise了 */
            /** 
             * 判断是走成功还是走失败 
             */
            then.call(x, y => { // 走成功
              if (called) return;
              called = true;
    
              // resolve(y);
              resolvePromise(promise2, y, resolve, reject); //有可能返回的promise的resolve中又传入promise
            }, r => { // 走失败
              if (called) return;
              called = true;
                
              reject(r);
            })
          } else { /** 不是promise, 常量直接抛出*/ 
            resolve(x);
          }
          
        } catch (err) {
          if (called) return;
          called = true;
    
          reject(e); // 取then抛出异常就报错好了
        }
    
      } else { //** 不是promise*/ 
        resolve(x);
      }
    }
    class Promise{
      constructor(executor) {
        this.state = PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        const resolve = (value) => {
          if (this.state === PENDING) {
            this.state = FULFILLED;
            this.value = value;
            this.onResolvedCallbacks.forEach(fn => fn())
          }
        }
        const reject = (reason) => {
          if (this.state === PENDING) {
            this.state = REJECTED;
            this.reason = reason;
            this.onRejectedCallbacks.forEach(fn => fn())
          }
        }
        try {
          executor(resolve, reject)
        } catch (err) {
          reject(this.reason)
        }
      }
      then(onFulfilled, onRejected) {
        // 如果then的两个回调不是函数或者不传做处理
        onFulfilled = typeof onFulfilled === 'function'?onFulfilled:val=>val;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        
        let promise2 = new Promise((resolve, reject) => {
          let x = undefined;
          /**
           * x为上一个then返回值,
           * 上一个then, 1.抛出错误 或 2.返回失败promise,下一个then才走失败 (调用promise2的reject)
           * 否则都走成功(调用promise2的resolve)
           */ 
          try { 
            if (this.state === FULFILLED) {
              x = onFulfilled(this.value);
            }
            if (this.state === REJECTED) {
              x = onRejected(this.reason);
            }
            if (this.state === PENDING) {
              this.onResolvedCallbacks.push(() => {
                x = onFulfilled(this.value);
              });
              this.onRejectedCallbacks.push(() => {
                x = onRejected(this.reason);
              });
            }
            // resolve(x);  
    
          } catch (err) {  /** 1. 抛出错误走失败(reject)*/ 
            reject(err);
          }
          /** 2. 
           * 判断x是否为失败的promise
           * 如果x是promise,x会决定promise2是resolve还是reject
           */
          setTimeout(() => {  // 为了拿到正在new的promise2做参数,将其放入定时器中
            resolvePromise(promise2, x, resolve, reject); // 
          })
        })
        return promise2;
      }
    }
    module.exports = Promise;
    

    纠错:

    promise源码中:
    then函数中的两个回调需要放入异步(setTimeout)中,因为es6的Promise中的then方法回调是异步的


    参考来源珠峰架构

  • 相关阅读:
    Git的搭建和使用技巧完整精华版
    Apache配置虚拟主机
    php curl向远程服务器上传文件
    将树形结构的数组按照顺序遍历为二维数组
    编码-截取中文-去除HTML字符
    PHP最原始的上传文件函数
    PHP中获取当前页面的完整URL
    ethereum/EIPs-191 Signed Data Standard
    ethereum/EIPs-607 Hardfork Meta: Spurious Dragon硬分叉相关
    ethereum/EIPs-155 Simple replay attack protection 35,36
  • 原文地址:https://www.cnblogs.com/topyang/p/11389321.html
Copyright © 2011-2022 走看看