答:
JavaScript将任务的执行模式分为两种,一种是同步执行模式,另一种就是JS异步编程执行模式,我们都知道,JavaScript执行的环境是单线程,即在同一时间只能做一件事情,代码依次执行,如下:
console.log("task1")
console.log("task2")
console.log("task3")
console.log("task4")
代码会依次输出,task1、task2、task3、task4,安全、直观简洁,但如果接口响应时间比较长,则会出现程序“假死”的情况,如下,很大程度上降低了执行效率速度,效率低,响应时间长
console.log("task start")
while(i<1000){ //夸张演示
ajax("http://www.xxx.com/getData/i") //响应时间长
}
console.log("task end")
怎样做到异步编程?
回调函数。通过回调函数的形式将所要执行的函数放到任务队列中,不占用主线程,只有等主线程执行完毕之后,才通知请求执行任务,从而将任务从任务队列进入到主线程中执行。异步方案也在不断的发展,callback => promise => gennerator => async/await…
Event Loop,顾名思义为:事件循环,是我们使用异步的原理,是指浏览器一种解决JS单线程运行时的一种机制。
console.log(0)
setTimeout(()=>{
console.log(1)
},10)
console.log(2)
//结果:0、2、1
上图
步骤分为:
同步任务在主线程执行,形成一个执行栈
在主线程之外,还存在一个任务队列,通过回调函数的形式将所要执行的函数放到任务队列中,不占用主线程
等主线程执行完毕后,系统会读取"任务队列",从而将任务从任务队列进入到主线程中执行。
主线程循环执行第三步
主线程从任务队列中读取事件,这个过程是不断循环的,所以整个运行机制被称为event loop
栈、堆、消息队列是一种数据结构,队列,特点为先进先出,存放执行的任务
任务分为:宏任务和微任务
宏任务:消息队列中的每个任务都是宏任务,
微任务:每个宏任务对应都有一个微任务队列
执行栈在完成同步任务之后,会去执行任务队列中的宏任务,每次宏任务执行完毕之后,查看相应的微任务队列是否为空,如果不为空,则会按照先进先出的规则全部执行完对应的微任务,如此循环,直至任务结束。
微任务主要解决任务优先级的问题和单个任务执行过长的问题
举个栗子:
setTimeout(function () {
console.log('1')
}); //宏任务
new Promise(function (resolve) {
console.log('2');
resolve();
}).then(function () { //then、微任务
console.log('3')
});
console.log('4');
//2、4、3、1
因为settimeout是宏任务,虽然先执行的他,但是他被放到了宏任务的消息队列里,然后代码继续往下检查看有没有微任务,检测到Promise的then函数被放入了微任务序列。等到主线进程的所有代码执行结束后。先从微任务queue里拿回掉函数,然后微任务queue空了后再从宏任务的queue拿函数。所以正确的执行结果是:2,4,3,1。
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(msg) {
var promise = new Promise((resolved, rejected) => {
setTimeout(()=>{
resolved(msg)
}, 10)
});
return promise;
}
fn().then(function () {
return fn("hello")
}).then(value=> {
return fn(value+"lagou")
}).then(value=>{
return fn(value+"I ♥ U")
}).then(value=>{
console.log(value)
})
const cars = [
{name:'Ferrair FF',horsepower:660,dollar_value:700000,in_stock:true},
{name:'Spyker C12 Zagato',horsepower:650,dollar_value:648000,in_stock:false},
{name:'Jafuar 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 isLaskInStock = function (cars){
let last_car = fp.last(cars)
return fp.prop('in_stock',last_car)
}
答:
let fr = fp.flowRight(fp.prop('in_stock'),fp.last)
console.log(fr(cars)) // false
2、使用函数组合fp.flowRight()、fp.prop()和fp.first()获取第一个car的name
答:
let fr = fp.flowRight(fp.prop('name'),fp.first)
console.log(fr(cars)) // Ferrair FF
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 a = averageDollarValue(cars)
console.log(a)
答:
let _average = function (xs){
return fp.reduce(fp.add,0,xs) / xs.length
}
let averageDollarValue = function(cars){
let arr = fp.flowRight(_average,fp.map(car=>car.dollar_value))
return arr(cars)
}
4、使用flowRight写一个sanitizeNames()函数,返回一个下划线连接的小写字符串,把数组中的name转换为这种形式,例如:sanitizeNames([‘Hello World’]) => [‘Hello_world’]
let _underscore = fp.replace(/\W+/g,"_") // 无需改动
答:
function sanitizeNames(){
return fp.flowRight(fp.map(name=>_underscore(name)))
}
// var name = ["hello world","bei jing"]
// console.log(sanitizeNames()(name)) // 結果[ 'hello_world', 'bei_jing' ]
三、基于下面提供的代码,完成后续的四个练习
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
const fp = require('lodash/fp')
const { parseInt } = require("lodash")
let maybe = MayBe.of([5,6,1])
let ex1 = () =>{
//补充
}
console.log(ex1(3))
答:
let ex1 = (num) =>{
let fn = fp.flowRight(fp.map(fp.add(num)))
return maybe.map(fn)
}
console.log(ex1(3)) // [8,9,4]
2、实现一个函数ex2,能够使用fp.first获取列表的第一个元素
let xs = Container.of(['do','ray','me','fa','so','la','ti','do'])
let ex2 = () => {
//你要实现的函数
}
答:
let ex2 = () => {
return xs.map(fp.first)._value
}
console.log(ex2()) // do
3、实现一个函数ex3,使用safeProp和fp.first找到user的名字的首字母
答:
let ex3 = () => {
return safeProp('name',user).map(fp.first)._value
}
console.log(ex3()) // A
4、使用MayBe重写ex4,不要有if语句
let ex4 = function(n){
if(n){
return parseInt (n)
}
}
答:
let ex4 = function(n){
let m1 = new MayBe(n)
let m2 = m1.map(parseInt)
return m2._value
}
ex4() //undefined
ex4(1) // 1
四、手写实现MyPromise源码
要求:尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理
/**
* 1、promise是一个类,在执行这个类的时候,需要传递一个执行器进去,执行器会立即执行
* 2、Promise中有三个状态,分别为 成功 resolve、失败 reject、等待 pedding
* 状态一旦确定就不能被改变
* pedding-resolve
* pedding-reject
* 3、resolve和reject函数是用来更改状态的
* resolve:fufilled
* reject:rejected
* 4、then方法做的事情就是判断状态,如果状态是成功,调用成功回调函数,如果是失败,调用失败函数,then方法是被定义在原型对象中
* 5、then成功回调有一个参数,表示成功之后的值,失败回调有一个参数,表示失败的原因
*/
const PEDDING = 'pedding' //等待
const FUFILLED = 'fufilled' //成功
const REJECT = 'reject' //失败
class MyPromise {
constructor(exeuctor) {
try {
exeuctor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
status = PEDDING
//成功之后的值
value = undefined
//失败之后的原因
reason = undefined
//成功回调
// successCallback = undefined 只能处理一个回调函数
successCallback = []
//失败回调
// failCallback = undefined
failCallback = []
//使用箭头函数定义是为了执行方法的时候让this指向MyPromise的实例对象
resolve = value => {
//如果状态不是等待,向下执行
if (this.status !== PEDDING) return
this.status = FUFILLED
//保存成功之后的值
this.value = value
//判断成功回调是否存在,如果存在则调用
// this.successCallback && this.successCallback(this.value)
while (this.successCallback.length) {
// this.successCallback.shift()(this.value)
this.successCallback.shift()()
}
}
reject = reason => {
if (this.status !== PEDDING) return
this.status = REJECT
//保存失败后的原因
this.reason = reason
// this.failCallback && this.failCallback(this.reason)
while (this.failCallback.length) {
// this.failCallback.shift()(this.reason)
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 === FUFILLED) {
// let x = successCallback(this.value)
/**
* 需要判断x的值是普通值还是promise对象,如果是普通值,直接调用resolve,
* 如果是promise对象,查看promise的结果,根据promise对象返回的结果决定调用resolve,reject
*/
// resolvePromise(x,resolve,reject)
//防止循环调用,但是此时promise2并不能获取到,所以现在需要使其变成异步执行代码
// resolvePromise(promise2,x,resolve,reject)
//使用try-catch捕获异常
try {
setTimeout(() => {
let x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
}, 0)
} catch (error) {
reject(error)
}
} else if (this.status === REJECT) {
setTimeout(() => {
let x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
}, 0)
} else {
//状态为pedding,等待
// 将成功回调和失败回调存储起来
// this.successCallback.push(successCallback)
this.successCallback.push(() => {
setTimeout(() => {
let x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
}, 0)
})
// this.failCallback.push(failCallback)
this.failCallback.push(() => {
setTimeout(() => {
let x = failCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
}, 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 = []
return new MyPromise((resolve, reject) => {
let count = 0
function addData(index,value){
result[index] = value
count++
console.log(count,array.length)
if(count === 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,current)
}
}
})
}
static resolve(value){
if(value instanceof MyPromise){
return value
}
return new MyPromise(resolve=>resolve(value))
}
}
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError("Chaining cycle detected for promise # " ))
}
if (x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
module.exports = MyPromise