简答题
一、谈谈你是如何理解JS异步编程,EventLoop、消息队列都是做什么的,什么是宏任务,什么是微任务?
1.JS异步编程
要理解JS的异步编程,首先要知道JavaScript是单线程执行机制, 由于单线程的执行机制,意味着没有线程协同处理任务,那么势必在一堆同步任务队列下,面对一些耗时的任务时, 会发生阻塞,导致处理效率不佳。
JS异步编程的首要任务就是来提高JS单线程执行机制下的处理效率的,针对一些耗时操作及宿主环境下的api交互, 例如接口的请求调用,文件的读写操作,消息发送及接收,通过异步编程的方式,都能得到很好的提升。
常用的JS异步编程的处理方式有回调函数、事件、Promise、Generator、Async Await
2. EventLoop、消息队列
Wikipedia这样定义EventLoop:Event Loop是一个程序结构,用于等待和发送消息和事件。简单说,就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。
在JavaScript中,主线程在执行过程中遇到了异步任务,就发起函数或者称为注册函数,通过event loop线程通知相应的工作线程(如ajax,dom,setTimout等),同时主线程继续向后执行,不会等待。等到工作线程完成了任务,eventloop线程会将消息添加到消息队列中,如果此时主线程上调用栈为空就执行消息队列中排在最前面的消息,依次执行。 新的消息进入队列的时候,会自动排在队列的尾端。单线程意味着js任务需要排队,如果前一个任务出现大量的耗时操作,后面的任务得不到执行,任务的积累会导致页面的“假死”。这也是js编程一直在强调需要回避的“坑”。主线程会循环上述步骤,EventLoop就是主线程重复从消息队列中取消息、执行的过程。
3.宏任务、微任务
宏任务有:script、setTimeout、setInterval、setImmediate(浏览器暂时不支持,只有IE10支持)、I/O、UI Rendering。
微任务有:Process.nextTick(node)、Promise、MutationObserver。
代码题
一、将下面异步代码使用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 fn(value) {
const promise = new Promise((resolved, rejected) => {
setTimeout(()=>resolved(value), 10)
});
return promise;
}
fn()
.then(() => fn("hello"))
.then(value => fn(value + "lagou"))
.then(value => fn(value + "I ❤ U"))
.then(value => console.log(value))
二、基于以下代码完成下面的四个练习
const fp = require('lodash/fp')
// 数据
// horsepower 马力, dollar_value 价格, in_stock 库存
const cars = [
{ name: 'Ferrari FF', horsepower: 660,
dollar_value: 700000, in_stock: true },
{ name: 'Spyker C12 Zagato', horsepower: 650,
dollar_value: 648000, in_stock: false },
{ name: 'Jaguar XKR-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 = function (cars) {
// 获取最后一条数据
let last_car = fp.last(cars)
// 获取最后一条数据的 in_stock 属性值
return fp.prop('in_stock', last_car)
}
实现:
let isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last)
练习2:使用fp.flowRight()、fp.prop()和fp.first()获取第一个car的name
实现:
let isFirstCarName = fp.flowRight(fp.prop('name'), fp.first)
练习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)
}
实现:
let averageDollorValue = fp.flowRight(_average, fp.map(fp.curry(fp.prop)('dollar_value')))
练习4:使用flowRight写一个sanitizeNames()函数返回一个下划线连接的小写字符串,把数组中的name转换为这种形式:例如:sanitizeNames(["Hello World"])=>["hello_world"]
let _underscore = fp.replace(/\W+/g, '_') // <-- 无须改动,并在 sanitizeNames中使用它
实现:
let sanitizeNames = fp.flowRight(fp.map(_underscore), fp.map(fp.toLower), fp.map(car => car.name))
三、基于下面提供的代码,完成后续的四个练习
// 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 }
练习1:使用fp.add(x,y)和fp.map(f,x)创建一个能让functor里的值增加的函数ex1
// app.js
const fp = require('lodash/fp')
const { Maybe, Container } = require('./support')
let maybe = Maybe.of([5, 6, 1])
let ex1 = () => {
// 你需要实现的函数。。。
}
实现:
let ex1 = () => {
return maybe.map(arr => fp.map(v => fp.add(v, 1), arr))
}
练习2:实现一个函数ex2,能够使用fp.first获取列表的第一个元素
// app.js
const fp = require('lodash/fp')
const { Maybe, Container } = require('./support')
let xs = Container.of(['do', 'ray',
'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {
// 你需要实现的函数。。。
}
实现:
let ex2 = () => {
return xs.map(fp.first).value
}
练习3:实现一个函数ex3,使用safeProp和fp.first找到user的名字的首字母
// app.js
const fp = require('lodash/fp')
const { Maybe, Container } = require('./support')
let safeProp = fp.curry(function (x, o){
return Maybe.of(o[x])
})
let user = { id:2, name: 'Albert' }
let ex3 = () => {
// 你需要实现的函数。。。
}
实现:
let ex3 = () => {
return safeProp('name', user).map(fp.first)._value
}
练习4:使用Maybe重写ex4,不要有if语句
// app.js
const fp = require('lodash/fp')
const { Maybe, Container } = require('./support')
let ex4 = function (n) {
if(n) {
return parseInt(n)
}
}
实现:
let ex4 = n => Maybe.of(n).map(parseInt)._value;
四、手写实现MyPromise源码
要求:尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor (excutor) {
try {
excutor(this.resolve, this.reject);
} catch (error) {
this.reject(error)
}
}
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(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(reason);
while(this.failCallback.length) this.failCallback.shift()()
}
then (successCallback, failCallback) {
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
let promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象 查看promise对象的返回结果
// 再根据promise对象的返回结果,决定调用resolve 还是reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象 查看promise对象的返回结果
// 再根据promise对象的返回结果,决定调用resolve 还是reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象 查看promise对象的返回结果
// 再根据promise对象的返回结果,决定调用resolve 还是reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值,直接调用resolve
// 如果是promise对象 查看promise对象的返回结果
// 再根据promise对象的返回结果,决定调用resolve 还是reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
});
}
});
return promise2;
}
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) {
current.then(value => addData(i, value), reaseon => reject(reason));
} else {
addData(i, current)
}
}
})
}
static resolve (value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value));
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
if (x instanceof MyPromise) {
// x.then(value => resolve(value), reason => reject(reason))
x.then(resolve, reject);
} else {
resolve(x);
}
}
module.exports = MyPromise
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!