练习一

117 阅读6分钟

一. 谈谈你是如何理解JS异步编程的,EventLoop是做什么的、消息队列是做什么的,什么是宏任务,什么是微任务? JS是个单线程的执行的,通过队列的形式,代码会完成一个任务后再去执行下一个任务即我们常说的'同步模式'。但是有些任务比较耗时,比如定时器,如果我们等待这些任务执行完了,再去执行下一个任务,代码执行就会相当耗时,为了解决这个问题就提出了‘异步模式’。所以代码执行是存在同步和异步两种模式,那么怎么去确保同步和异步代码正常执行,且耗时最短,为了运行不阻塞,就采用了事件循环的模式,即EventLoop。 事件循环(EventLoop)处理方式是把同步任务放到主线程中一次执行,遇到了异步任务,则会把异步任务当道消息队列中等待执行,等主线程任务执行完成之后,执行栈清空,从消息队列中取出一个任务放到执行栈继续执行,执行完成之后继续上一步操作,指导全部执行完成 消息队列是来处理异步任务的,从上文我们可以知道,每当遇到异步任务,都会放入消息队列中,执行完毕后,有消息队列通知主线程排队执行 宏任务可以理解为任务队列的任务,包含外层同步代码script,setTimeout/setInterval,setImmediate(IE/node),requestAnimationFrame,I/O,UI rendering,注册事件。 微任务当主线程任务执行完成之后,立即执行,当前宏任务执行完成之后,立即执行,不会等待整个任务。在执行时能获取任务外的上下文,常见的包含Promise.then catch finally,MutaionObserve,process.nextTick(node)等

1.代码题 一. 将下面异步代码使用Promise方法

setTimeout(function(){
    var a = 'hello';
    setTimeout(function(){
        var b = 'lagou'
        setTimeout(function(){
            var c = 'I ❤ U'
            console.log(a + b + c)
        },10)
    },10)
},10) 

改写:

function initPromise(time){
    return function(str){
        return new Promise((resolve,reject) => {
            setTimeout(()=>{
                resolve(str);
            },time)
        })
    }
}
let p = initPromise(10);
Promise.all([p('hello'),p('lagou'),p('I ❤ U')]).then(value=>{
    const result = value.reduce((total,curr)=>{return total + curr},'');
    console.log(result);
});

二. 基于一下代码完成四个练习

const fp = require("lodash/fp");
const cars = [
    {name: 'Ferrari FF',horsepower: 600,dollar_value: 700000,in_stock: true},
    {name: 'Spyker C12 Zagato',horsepower: 650,dollar_value: 648000,in_stock: false},
    {name: 'Jaguar XXR_S',horsepower: 550,dollar_value: 132000,in_stock: false},
    {name: 'Audi R8',horsepower: 525,dollar_value: 114200,in_stock: false},
    {name: 'Aston Martin One-77',horsepower: 750,dollar_value: 1850000,in_stock: true},
    {name: 'Pagani Huayra',horsepower: 700,dollar_value: 1300000,in_stock: false},
];

练习1:使用函数组合fp.flowRight()重新实现下面这个函数 实现为

let isLastInStock = fp.flowRight(fp.prop('in_stock'),fp.last);
console.log(isLastInStock(cars));

练习2: 实现为

const isFirstName = fp.flowRight(fp.prop('name'),fp.first);
console.log(isFirstName(cars));

练习3: 实现为

let _average = xs => fp.reduce(fp.add, 0, xs)/xs.length
let averageDollarValue = fp.flowRight(_average,fp.map(fp.prop('dollar_value')));
console.log(averageDollarValue(cars));

练习4: 实现为

let _underscore = fp.replace(/\W+/g,'_');
let sanitizeNames = fp.map(fp.flowRight(_underscore,fp.lowerCase));
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}
//app.js
const fp = require('lodash/fp');
const {Maybe,Container} = require("./support");
let maybe = Maybe.of([5,6,1]);

/* 练习1. 使用fp.add(x,y) 和fp.map(f,x)创建一个能让functor里的值增加的函数ex1 */

let ex1 = () =>{
    //你需要实现的函数
    return (y)=>{
       maybe = maybe.map(x => fp.map(val => fp.add(y,val),x))
    }
}
let add = ex1();
add(2);
maybe.map(x => {console.log(x)})

/* 练习2. 实现一个函数ex2 ,能够使用fp.first获取列表的第一个元素 */

let xs = Container.of(['do','ray','me','fa','so','la','ti','do']);
let ex2 = () => {
    //你需要实现的函数
    let b;
    xs.map(x => {b = fp.first(x)})
    return b
}
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'}
let ex3 = () => {
    //你需要实现的函数
    /* 获取名字 */
    let userInfo = safeProp('name',user);
    userInfo.map(name => {
        console.log(name); //名字
        console.log(fp.first(name)); //首字母
    })
}
ex3();

/* 练习4.使用Maybe重写ex4, 不要有if语句*/

// let ex4 = function(n){
//     if(n){
//         return parseInt(n)
//     }
// }
let ex4 = n => {
    let num;
    let _num = Maybe.of(n);
    _num.map(x => {num = parseInt(x)});
    return n;
} 
console.log(ex4());

四. 手写实现MyPromise源码 要求:尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理

/* 
 * 1. Promise对象的then方法会返回一个全新的promise对象
 * 2. 后面的promsie是为前一个then方法返回的promise注册回调对象
 * 3. 前面then方法的返回值会作为后面then方法回调的参数
 * 4. 如果前一个then方法返回的是promise方法,那么后面的then方法会等待它的执行结束
 * 5. promise.resolve传递一个promsie对象,得到的是原promise对象
 * 6. promise的回调会作为微任务执行,不会作为宏任务重新回到队列排队,同理还有process.nextTick(node)与MutationObserver
 */
/**
 * promise then方法里面第二个参数捕获异常,与catch捕获异常的区别
 * 1. promise then方法里面的异常捕获,只是对上一个promise异常进行注册的回调函数,所以它只能捕获上一个promise里面的错误,无法捕获then方法里面的错误
 * 2. 使用catch是then返回的promise的异常捕获注册的回调函数,符合链式调用,而promise里面的错误,会随链式传递一直到捕获到为止,所以它不仅能捕获到上一个promise里面的错误
 *  而且能捕获到then方法里面的错误,建议用第二种
 */
/**
 * Promise.all (等待所有任务结束返回) 与 Promise.race (等待第一个任务结束就返回)
 */

// 模拟Promise类
var PENDING = 'pending'; //等待
var FULFILLED = 'fulfilled'; //成功
var REJECTED = 'rejected'; //失败
class myPromise {
	constructor(fn) {
		fn(this.resolve, this.reject);
	}
	//promise状态
	status = PENDING;
	//成功的值
	value = undefined;
	//失败的值
	err = undefined;
	//成功回调
	successFn = [];
	//失败回调
	failFn = [];
	//成功回调
	resolve = value => {
		//如果已经执行就不继续往下执行
		if (this.status !== PENDING) return;
		this.status = FULFILLED;
		this.value = value;
		// this.successFn && this.successFn(this.value);
		while (this.successFn.length) this.successFn.shift()();
	}
	//失败回调
	reject = err => {
		if (this.status !== PENDING) return;
		this.status = REJECTED;
		this.err = err;
		// this.failFn && this.failFn(err);
		while (this.failFn.length) this.failFn.shift()();
	}
	// then方法实现
	then(successFn, failFn) {
		//判断方法存不存在,不存在默认一个方法
		successFn = successFn ? successFn : val => val;
		failFn = failFn ? failFn : err => {throw err};
		let promise2 = new myPromise((resolve, reject) => {
			if (this.status == FULFILLED) { //执行成功的回调
				//判断x的值是普通值还是promise对象
				//如果是普通值调用resolve
				//如果是promise对象,查看promise对象的结果,再根据结果决定调用resolve还是调用reject
				//我们要判断是否等于自身,reject抛出错误;利用setTimeout异步传入当前promise
				//利用try/catch判断自身是否出现语法错误,reject当前错误
				setTimeout(() => {
					try {
						let x = successFn(this.value);
						resolvePromise(promise2, x, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0)
			} else if (this.status == REJECTED) { //执行失败的回调
				setTimeout(() => {
					try {
						let x = failFn(this.err);
						resolvePromise(promise2, x, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0)

			} else {
				this.successFn.push(() => {
					setTimeout(() => {
						try {
							let x = successFn(this.value);
							resolvePromise(promise2, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0)
				});
				this.failFn.push(() => {
					setTimeout(() => {
						try {
							let x = failFn(this.err);
							resolvePromise(promise2, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0)
				});
			}
		});
		return promise2
	}
	// promise.all接受一个数组,可以是promise对象,可以是值
	// 返回一个promise,且成功回调里面的值是数组,根据传入的值的位置确定,不受异步影响
	static all(arr) {
		//返回数组
		let result = [];
		let index = 0;
		return new myPromise((resolve, reject) => {
			//记录原位置,添加到数组
			//出现错误里面reject不再执行
			function addData(key, value) {
				result[key] = value;
				index ++;
				if(index === arr.length) resolve(result);
			}
			for (let i = 0; i < arr.length; i++) {
				let curr = arr[i];
				if (curr instanceof myPromise) { //promise对象
					curr.then(val => addData(i,val),err => reject(err))
				}else{ //普通值
					addData(i,curr);
				}
			}
		})
	}
	//finally方法无论成功还是失败都返回
	finnally(callback){
		return this.then(val =>{
			return myPromise.resolve(callback()).then(() => val)
		},err =>{
			return myPromise.resolve(callback()).then(() => {throw err})
		})
	}
	//catch捕获错误
	catch(failFn){
		return this.then(undefined,failFn)
	}
}

function resolvePromise(promise2, x, resolve, reject) {
	if (x === promise2) {
		return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
	}
	if (x instanceof myPromise) { //属于promise对象
		return x.then(resolve, reject);
	} else { //属于普通值
		resolve(x);
	}
}
// var promise = new myPromise((resolve, reject) => {
// 	resolve(4);
// });
// promise.then(val=>{console.log(val)});
// myPromise.all([1,promise,9]).then(val => {console.log(val)});
// promise.then(value=>{
// 	console.log(value);
// },err=>{
// 	console.log(err);
// })
// promise.then(()=>{
// 	console.log("执行第二次!")
// })
module.exports = myPromise;