前端训练营part1-1作业答案

本文介绍了JavaScript的异步编程原理,包括同步任务、异步任务、宏任务和微任务,以及EventLoop时间循环。同时,通过Promise和函数式编程库fp的实例,展示了如何改进异步操作和实现函数组合。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简答题
一、js异步编程实际是将部分js代码的执行滞后。js是单线程的,如果没有异步,对于某些耗时较大的操作,如异步获取数据,IO操作等,js将等待操作完成再执行下面的代码,这样会造成界面卡顿、白屏等现象。而js的异步编程就是用来解决这个问题的。
js在执行时,会将js代码解析成一个一个任务,然后依次执行。任务分为同步任务和异步任务。

  • 同步任务
    js在执行同步任务时会将任务添加到执行栈中并立即执行,等待任务执行完成之后再将其从执行栈中弹出。
  • 异步任务
    js在执行异步任务时,会经过相应的处理模块处理之后添加到任务队列中(也称消息队列)。在执行栈为空时,会从任务队列头部取出一个任务放到执行栈中执行,执行完成之后将其从执行栈中弹出。执行栈为空之后,会再次从任务队列里头部取出一个任务放到执行栈中执行,这个过程称为时间循环(即EventLoop)。

异步任务又分为宏任务和微任务

  • 宏任务:需要进入到任务队列中重新排队的任务,如setTimeout、setInterval等
  • 微任务:不需要重新进入到任务队列中重新排队的任务,如Promise、MutationObserver以及node中的process.nextTick。

执行栈为空之后,首先会从微任务队列中取出任务执行,待微任务队列为空之后再从宏任务队列中取出任务执行。
代码题
一、
1、使用Promise改进setTimeout

const p = val => new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(val)
  }, 10)
});

p('hello').then(val => {
  return p('lagou').then(value => val + value)
}).then(val => {
  return p('I ♥ U').then(value => val + value)
}).then(console.log);

二、
1、使用fp.flowRigth重新实现isLastInStock

const isLastInStock = fp.flowRight(fp.props('in_stock'), fp.last);

2、使用fp.flowRight、fp.props、fp.first获取第一个car的name

const isFirstInName = fp.flowRight(fp.props('name'), fp.first);

3、使用函数组合实现averageDollarValue

const averageDollarValue1 = fp.flowRight(_average, fp.map(car => {
  return car.dollar_value;
}));

4、使用fp.flowRight实现一个能让数组中name的转换为下划线连接的小写字母字符串

const sanitizeNames = fp.map(fp.flowRight(_underscore, fp.lowerCase, car => car.name));

三、
1、使用fp.add和fp.map创建一个能让functor里面的值增加的函数

const ex1 = (y) => {
  return maybe.map(fp.map(fp.add(y)));
}

2、使用fp.first获取列表的首字母

const ex2 = () => {
  return xs.map(fp.first)
}

3、使用safeProp和fp.first找到user的name的首字母

const ex3 = () => {
  return safeProp('name')(user).map(fp.first)
}

4、重写ex4,不要有if

const ex4 = (n) => {
  return Maybe.of(n).map(parseInt)
}

四、手动实现Promise

/**
 *
 * promise功能实现:
 *  1、Promise是一个对象,接收一个处理器函数并立即执行
 *  2、处理器函数接收两个参数,resolve和reject。resolve将状态改为成功,reject将状态改为失败。
 *  3、Promise有三个状态,pending:初始等待状态,fulfilled:成功,rejected:失败,
 *    并且只能由pending -> fulfilled 或 由 pending -> rejected。
 *    状态一旦改变就不能再次改变。
 *  4、原型方法then接收两个参数,更具状态判断执行哪个参数。
 *    fulfilled 执行第一个成功的回调函数,并将值作为参数传递,值是调用executor中resolve传入的。
 *    rejected 执行第二个失败的回调函数,并将失败原因作为参数传递,失败的原因是executor中调用reject传入的。
 *    then的回调函数返回值的一些情况:
 *      1、返回一个值,则then返回一个成功状态的Promise,并将返回值作为成功回调的参数
 *      2、没有返回值,则then返回一个成功状态的Promise,并将undefined作为成功回调的参数
 *      3、抛出一个错误,则then返回一个失败状态的Promise,并将错误对象作为失败回调的参数
 *      4、返回一个成功状态的Promise,则then也会返回一个成功状态的Promise,并将Promise执行函数中的resolve的参数作为then的成功回调函数的参数
 *      5、返回一个失败状态的Promise,则then也会返回一个失败状态的Promise,并将Promise执行函数中的reject的参数作为then的失败回调函数的参数
 *      6、返回一个等待状态的Promise,则then也会返回一个等待状态的Promise,并且在改变状态之后将使用Promise执行函数中的resolve/reject的参数作为对应回调函数的参数
 */

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    // 执行器错误捕获处理
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e);
    }
  }
  // promise 的状态
  status = PENDING;
  // 保存值
  value = undefined;
  // 保存失败的原因
  reason = undefined;
  // 成功回调函数
  successCallBacks = [];
  // 失败回调函数
  failCallBacks = [];
  resolve = (value) => {
    // 判断如果状态不是 pending,直接返回,状态一旦改变,并能再次改变
    if(this.status !== PENDING) return;
    // 保存 promise 的状态为 成功
    this.status = FULFILLED;
    // 保存值
    this.value = value;
    // 如果保存有异步回调,从第一个开始一次执行
    while(this.successCallBacks.length) {
      this.successCallBacks.shift()()
    }
  };
  reject = (reason) => {
    // 判断如果状态不是 pending,直接返回,状态一旦改变,并能再次改变
    if(this.status !== PENDING) return;
    // 保存 promise 的状态为 失败
    this.status = REJECTED;
    // 保存失败的原因
    this.reason = reason;
    // 如果保存有异步回调,从第一个开始一次执行
    while(this.failCallBacks.length) {
      this.failCallBacks.shift()()
    }
  };
  then(successCallback, failCallback) {
    // 参数处理
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback ? failCallback : reason => { throw reason; };
    // 返回一个Promise
    let resultPromise = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED) { // 成功
        // 将同步代码变成异步代码,保证resultPromise能取到正确的值
        // 这里不能将setTimeout放到方法中,因为方法执行时resultPromise还没有取到最新值
        setTimeout(() => {
          thenCodeExecute(successCallback, this.value, resultPromise, resolve, reject)
        }, 0)
      } else if(this.status === REJECTED) {// 失败
        // 将同步代码变成异步代码,保证resultPromise能取到正确的值
        setTimeout(() => {
          thenCodeExecute(failCallback, this.reason, resultPromise, resolve, reject)
        }, 0)
      } else { // 等待状态,存在异步回调
        // 保存异步回调函数,可能存在多个then调用
        this.successCallBacks.push(() => {
          setTimeout(() => {
            thenCodeExecute(successCallback, this.value, resultPromise, resolve, reject)
          }, 0)
        });
        this.failCallBacks.push(() => {
          // 将同步代码变成异步代码,保证resultPromise能取到正确的值
          setTimeout(() => {
            thenCodeExecute(failCallback, this.reason, resultPromise, resolve, reject)
          }, 0)
        })
      }
    });
    return resultPromise;
  }
  /**
   * 接收一个回调函数,返回一个promise对象,promise对象和前一个then返回的promise的状态和值都一致
   * 回调函数不论成功还是失败状态都会执行,如果回调函数返回一个promise,则会等待promise执行完成再执行后面的调用
   * @param callback
   * @returns {*}
   */
  finally(callback) {
    // 调用then方法确定promise的状态,并进行对应的处理
    return this.then(value => {
      // 将回调函数的结果转化成Promise对象,再通过Promise的then方法将value/reason向下传递
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => {throw reason;});
    })
  }
  /**
   * 返回一个失败状态的promise,并将callback的结果向下传递
   * @param callback
   * @returns {*}
   */
  catch(callback) {
    return this.then(undefined, callback)
  }
  /**
   * 合并执行Promise,当所有传入的promise都成功时,将返回成功状态的Promise对象,并将所有Promise的结果传入resolve中
   * 当有一个Promise的状态为失败时,返回一个失败状态的Promise对象
   * @param array
   * @returns {MyPromise}
   */
  static all(array) {
    const r = [];
    let index = 0;
    const len = array.length;
    return new MyPromise((resolve, reject) => {
      function addElement(key, data) {
        r[key] = data;
        index++;
        // 判断结果集中元素个数是否和参数中数组的元素数量一致,如果一致则表示所有Promise都执行完毕
        // 因为普通元素会先一步存入到结果集中(因为是同步的),而Promise是异步的,所以for循环完成之后才开始将成功的结果放入到结果集中。
        if(index === len) {
          resolve(r);
        }
      }
      for (let i = 0; i < len; i++) {
        const current = array[i];
        if (current instanceof MyPromise) { // Promise对象,成功时将返回值存入到结果集中,失败时将返回的Promise的状态变成失败,并将原因传入
          // 闭包保存 i 的值
          current.then(val => addElement(i, val), reason => reject(reason))
        } else { // 普通值,直接将当前数组元素存入到结果的对应位置
          addElement(i, current)
        }
      }
    })
  }
  /**
   * 将传入的参数转化为一个Promise对象,如果参数本身是一个Promise对象,将原样返回
   * 如果参数不是一个Promise对象,将作为成功状态的返回值向下传递
   * @param value
   * @returns {MyPromise}
   */
  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value))
  }
  // 返回一个带有拒绝原因的Promise对象
  static reject(value) {
    return new MyPromise((resolve, reject) => reject(value))
  }
  
  /**
   * 接收一个数组,返回一个Promise,
   * 只要有一个Promise返回成功或失败状态,就确定了Promise的状态
   * 返回的Promise状态及参数和最先确定状态的Promise一致
   * 如果数组中的元素为非Promise对象,则立即返回一个成功状态Promise,resolve参数为该数组元素
   * @param array
   * @returns {MyPromise}
   */
  static race(array) {
    return new MyPromise((resolve, reject) => {
      for(let i = 0, len = array.length; i < len; i++) {
        const current = array[i];
        if (current instanceof MyPromise) {
          current.then(resolve, reject);
        } else {
          resolve(current);
        }
      }
    })
  }
}
/**
 * 获取then方法回调函数的返回结果,并根据结果确定then方法返回的Promise的状态及对应参数
 * @param callback 回调函数
 * @param value 回调函数执行时的参数
 * @param resultPromise 要返回的promise对象
 * @param resolve resultPromise执行器的resolve方法
 * @param reject resultPromise执行器的reject方法
 */
function thenCodeExecute(callback, value, resultPromise, resolve, reject) {
  try {
    resolvePromise(resultPromise, callback(value), resolve, reject);
  } catch (e) {
    reject(e);
  }
}

/**
 * 根据then方法回调函数的返回值,确定then方法返回的promise的状态及参数。
 * @param resultPromise then方法返回的promise对象
 * @param callbackResult then方法回调函数的返回值
 * @param resolve resultPromise执行器的resolve方法
 * @param reject resultPromise执行器的reject方法
 * @returns {*}
 */
function resolvePromise(resultPromise, callbackResult, resolve, reject) {
  // then的返回值返回了自身,将Promise状态变为reject,并传递错误原因。
  if (resultPromise === callbackResult) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (callbackResult instanceof MyPromise) {
    /**
     * 返回值是Promise对象,根据返回值Promise对象的状态将对应值传递给对应的回调函数
     * 1、返回值Promise状态是成功状态,将成功状态的返回值传递给resolve
     * 2、返回值Promise状态是失败状态,将失败状态的返回值传递给reject
     */
    callbackResult.then(resolve, reject)
  } else { // 返回值是普通值, 直接调用
    resolve(callbackResult)
  }
}

module.exports = MyPromise;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值