zoukankan      html  css  js  c++  java
  • 函数式编程与JS异步编程、手写Promise

    一、谈谈你是如何理解JS异步编程的,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?

    1. 异步编程:回调函数、事件监听、发布/订阅、Promises对象

    2. EventLoop是主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。消息队列是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。 

    3. 宏任务 Macrotasks 就是参与了事件循环的异步任务;微任务 Microtasks 就是没有参与事件循环的“异步”任务。

    代码题

    一、将下面异步代码使用Promise的方式改进

    setTimeout(function () {
        var a = "111"
        setTimeout(function () {
            var b = "222"
            setTimeout(function () {
                var c = "333"
                console.log(a + b + c)
            }, 10)
        }, 10)
    }, 10)

     修改后

    let test = new Promise((resolve, reject) => {
        try {
        setTimeout(() => {
            resolve()
        }, 100)
        } catch (error) {
            reject()
        }
    })
    
    test.then((result) => {
        return ['11']
    }).then((result) => {
        result.push('22')
        return result
    }).then((result) => {
        result.push('33')
        return result
    }).then((result) => {
        console.log(result.join(' '))
    })


    二、基于以下代码完成下面4个函数

    const _ = require("loadsh")
    const fp = require("loadsh/fp")
    const cars = [
        { name: "FF", horsepower: 660, dollar_value: 700000, in_stock: true },
        { name: "C12", horsepower: 650, dollar_value: 648000, in_stock: false },
        { name: "XKR-S", horsepower: 550, dollar_value: 132000, in_stock: false },
        { name: "R8", horsepower: 525, dollar_value: 114200, in_stock: false },
        { name: "One-77", horsepower: 750, dollar_value: 185000, in_stock: false },
        { name: "Huayra", horsepower: 700, dollar_value: 130000, in_stock: false },
    ]
    
    // 练习1:使用函数组合fp.flowRight()重新实现下面函数
    // let isLastInStock = function (cars) {
    
    //     let last_car = fp.last(cars)
    //     return fp.prop('in_stock', last_car)
    // }
    // console.log(isLastInStock(cars));
    
    // let lastCar = function (cars) {
    //     return fp.last(cars)
    // }
    // let carProp = function (car) {
    //     return fp.prop('in_stock', car)
    // }
    // const f = _.flowRight(carProp, lastCar)
    // console.log(f(cars))
    
    // 联系2:使用fp。flowRight()、fp.prop()和fp.first()获取第一个car的name
    // let firstCar = function (cars) {
    //     return fp.last(cars)
    // }
    // let carPropName = function (car) {
    //     return fp.prop('name', car)
    // }
    // const f = _.flowRight(carPropName, lastCar)
    // console.log(f(cars))
    
    // 联系3:使用帮助函数_average重构 averageDollarValue ,使用函数组合的方式实现
    let _average = function (xs) {
        return fp.reduce(fp.add, 0, xs) / xs.length
    }// <- 无需改动
    
    // let averageDollarValue = function (cars) {
    //     let dollar_values = fp.map(function (car) {
    //         return car.dollar_value
    //     }, cars)
    //     return _average(dollar_values)
    // }
    // console.log(averageDollarValue(cars));
    
    // 修改后:
    // let dollar_values = function (cars) {
    //     let dollar_values = fp.map(function (car) {
    //         return car.dollar_value
    //     }, cars)
    //     return dollar_values
    // }
    // let p = fp.flowRight(_average, dollar_values)
    // console.log(p(cars));
    
    // 联系4:使用flowRight写一个sanitizeNames()函数,返回一个下滑线连接的小写字符串,把数组中的name转换为这种形式:
    // 例如:sanitizeNames(["Hello World"]) => ["hello_world"]
    
    let _underscore = fp.replace(/W+/g, '_') // <--无需改动,并在sanitizeNames中使用它
    
    let toLower = s => fp.lowerCase(s)
    let sanitizeNames = fp.flowRight(_underscore, toLower)
    console.log(sanitizeNames(["Hello World"]));

     三、基于下面提供的代码,完成后续四个练习

    // support.js
    class Container {
        static of(value) {
            return new Container(value)
        }
        constructor(value) {
            this._value = value
        }
        map(fn) {
            return Container.of(fn(this._value))
        }
    }
    
    class Maybe {
        static of(x) {
            return new Maybe(x)
        }
        isNothing() {
            return this._value === null || this._value === undefined
        }
        constructor(x) {
            this._value = x
        }
        map(fn) {
            return this.isNothing() ? this : Maybe.of(fn(this._value))
        }
    }
    module.exports = { Maybe, Container }
    const fp = require("loadsh/fp")
    const { Maybe, Container } = require("./support")
    // console.log(Maybe);
    let maybe = Maybe.of([5, 6, 1])
    
    let ex1 = (num) => {
        // 函数实现
        return maybe.map((x) => {
            let a = fp.map(val => {
                return fp.add(val, num)
            }, x)
            return a
        })
    }
    console.log(ex1(1));
    
    
    
    // 练习2:实现一个函数ex2,能够使用fp.first获取列表的第一个元素
    let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do2'])
    let ex2 = () => {
        // 函数实现
        return xs.map(x => {
            return fp.first(x)
        })
    }
    console.log(ex2());
    
    
    
    // 练习3:实现一个函数ex3,使用safeProp和fp.first找到user的名字的首字母
    let safeProp = fp.curry(function (x, o) {
        return Maybe.of(o[x])
    })
    let user = { id: 2, name: 'Albert' }
    console.log(safeProp('name', user));
    let ex3 = () => {
        // 函数实现
        return safeProp('name', user).map(x => fp.first(x))
    }
    console.log(ex3());
    
    
    // 练习4:使用Maybe重写ex4,不要有if语句
    let ex4 = function (n) {
        // if (n) {
        //     return parseInt(n)
        // }
        // 实现
        return n ? parseInt(n) : undefined
    }

    四、手写实现MyPromise源码

    要求:尽可能还原Promise中的每个API,并通过注释的方式描述思路和原理。

    const PENDING = 'pending'; // 等待
    const FULFILLED = 'fulfilled'; // 成功
    const REJECTED = 'rejected'; // 失败
    
    class MyPromise {
      constructor (executor) {
        try {
          executor(this.resolve, this.reject)
        } catch (e) {
          this.reject(e);
        }
      }
      // promsie 状态 
      status = PENDING;
      // 成功之后的值
      value = undefined;
      // 失败后的原因
      reason = undefined;
      // 成功回调
      successCallback = [];
      // 失败回调
      failCallback = [];
    
      resolve = value => {
        // 如果状态不是等待 阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值
        this.value = value;
        // 判断成功回调是否存在 如果存在 调用
        // this.successCallback && this.successCallback(this.value);
        while(this.successCallback.length) this.successCallback.shift()()
      }
      reject = reason => {
        // 如果状态不是等待 阻止程序向下执行
        if (this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败后的原因
        this.reason = reason;
        // 判断失败回调是否存在 如果存在 调用
        // this.failCallback && this.failCallback(this.reason);
        while(this.failCallback.length) this.failCallback.shift()()
      }
      then (successCallback, failCallback) {
        // 参数可选
        successCallback = successCallback ? successCallback : value => value;
        // 参数可选
        failCallback = failCallback ? failCallback: reason => { throw reason };
        let promsie2 = new MyPromise((resolve, reject) => {
          // 判断状态
          if (this.status === FULFILLED) {
            setTimeout(() => {
              try {
                let x = successCallback(this.value);
                // 判断 x 的值是普通值还是promise对象
                // 如果是普通值 直接调用resolve 
                // 如果是promise对象 查看promsie对象返回的结果 
                // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                resolvePromise(promsie2, x, resolve, reject)
              }catch (e) {
                reject(e);
              }
            }, 0)
          }else if (this.status === REJECTED) {
            setTimeout(() => {
              try {
                let x = failCallback(this.reason);
                // 判断 x 的值是普通值还是promise对象
                // 如果是普通值 直接调用resolve 
                // 如果是promise对象 查看promsie对象返回的结果 
                // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                resolvePromise(promsie2, x, resolve, reject)
              }catch (e) {
                reject(e);
              }
            }, 0)
          } else {
            // 等待
            // 将成功回调和失败回调存储起来
            this.successCallback.push(() => {
              setTimeout(() => {
                try {
                  let x = successCallback(this.value);
                  // 判断 x 的值是普通值还是promise对象
                  // 如果是普通值 直接调用resolve 
                  // 如果是promise对象 查看promsie对象返回的结果 
                  // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                  resolvePromise(promsie2, x, resolve, reject)
                }catch (e) {
                  reject(e);
                }
              }, 0)
            });
            this.failCallback.push(() => {
              setTimeout(() => {
                try {
                  let x = failCallback(this.reason);
                  // 判断 x 的值是普通值还是promise对象
                  // 如果是普通值 直接调用resolve 
                  // 如果是promise对象 查看promsie对象返回的结果 
                  // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                  resolvePromise(promsie2, x, resolve, reject)
                }catch (e) {
                  reject(e);
                }
              }, 0)
            });
          }
        });
        return promsie2;
      }
      finally (callback) {
        return this.then(value => {
          return MyPromise.resolve(callback()).then(() => value);
        }, reason => {
          return MyPromise.resolve(callback()).then(() => { throw reason })
        })
      }
      catch (failCallback) {
        return this.then(undefined, failCallback)
      }
      static all (array) {
        let result = [];
        let index = 0;
        return new MyPromise((resolve, reject) => {
          function addData (key, value) {
            result[key] = value;
            index++;
            if (index === array.length) {
              resolve(result);
            }
          }
          for (let i = 0; i < array.length; i++) {
            let current = array[i];
            if (current instanceof MyPromise) {
              // promise 对象
              current.then(value => addData(i, value), reason => reject(reason))
            }else {
              // 普通值
              addData(i, array[i]);
            }
          }
        })
      }
      static resolve (value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value));
      }
    }
    
    function resolvePromise (promsie2, x, resolve, reject) {
      if (promsie2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
      }
      if (x instanceof MyPromise) {
        // promise 对象
        // x.then(value => resolve(value), reason => reject(reason));
        x.then(resolve, reject);
      } else {
        // 普通值
        resolve(x);
      }
    }
    
    module.exports = MyPromise;
  • 相关阅读:
    软件开发流程实例之四 :编码和测试
    软件开发流程实例之三 :系统设计
    jQuery入门[4]-链式代码
    jQuery入门[1]-构造函数
    jQuery入门[2]-选择器
    自编类库,添加引用,编译运行时显示“未能找到”
    SelectByShape 工具的实现
    TOC控件不显示内容列表
    鹰眼功能的实现(步骤,无代码)
    INumericFormat 接口
  • 原文地址:https://www.cnblogs.com/cssfirefly/p/14221439.html
Copyright © 2011-2022 走看看