前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >函数式编程与JS异步编程、手写Promise

函数式编程与JS异步编程、手写Promise

作者头像
deepcc
发布2021-01-05 15:01:11
1K0
发布2021-01-05 15:01:11
举报
文章被收录于专栏:deepcc

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

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

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

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

代码题

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

代码语言:javascript
复制
setTimeout(function () {
    var a = "111"
    setTimeout(function () {
        var b = "222"
        setTimeout(function () {
            var c = "333"
            console.log(a + b + c)
        }, 10)
    }, 10)
}, 10)

修改后

代码语言:javascript
复制
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个函数

代码语言:javascript
复制
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"]));

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

代码语言:javascript
复制
// 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 }
代码语言:javascript
复制
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,并通过注释的方式描述思路和原理。

代码语言:javascript
复制
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;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-01-02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、谈谈你是如何理解JS异步编程的,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?
  • 代码题
    • 一、将下面异步代码使用Promise的方式改进
      • 二、基于以下代码完成下面4个函数
        • 三、基于下面提供的代码,完成后续四个练习
          • 四、手写实现MyPromise源码
          相关产品与服务
          消息队列 CMQ 版
          消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档