高阶函数:一个函数的参数或返回值为函数时,这个函数就是高阶函数
- 作用:对原因函数进行扩展
function isType(typing) {
return (val) => {
return (Object.prototype.toString.call(val)) === `[object ${typing}]`
}
}
let utils = {};
['Number', 'Boolean', 'String'].forEach(type => {
utils[`is${type}`] = isType(type)
});
console.log(utils.isNumber('abc'))
函数柯理化:将一个函数的功能拆分成多个函数
- 作用:可以让函数变小,基于高阶函数,核心就是缓存变量
Promise
- 通过new Promise创建一个实例,Promise构造函数中接收一个回调参数excutor
- excutor函数接收两个参数resolve,reject
- 调用resolve会让promise变成成功状态,reject被调用会变成失败状态,状态一旦被确定成功/失败则不能改变
- 默认是pending 等待状态
- fufilled 成功状态
- rejected 失败状态
- 每个promise实例都有一个then方法,有两个参数
- onfufilled 成功回调
- onrejected 失败回调
- 如果不调用resolve/reject,则一直处于pending状态
- excutor是立即执行的
- promise是链式调用
- 返回非Promise的值/undefined,则返回成功状态的promise
- 返回reject/报错,则返回失败状态的promise
promise的实现
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class Promise {
constructor(executor) {
this.status = PENDING; // 默认是等待态
this.value = undefined; /// 成功的原因
this.reason = undefined; // 失败的原因
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
// promise调用then的时候 可能状态依旧是pending,那么我们需要将回调先存放起来
// 等待过一会调用resolve时触发 onResolvedCallbacks 执行
// 等待调用 reject时触发onRejectedCallbacks 执行
const resolve = (value) => {
if (value instanceof Promise) {
// 递归解析的流程
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach((cb) => cb());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((cb) => cb());
}
};
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
catch(errCallback) {
return this.then(null, errCallback);
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === "function"
? onRejected
: (e) => {
throw e;
};
let promise2 = new Promise((resolve, reject) => {
// 调用then的时候 已经确定了是成功还是失败了
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
});
return promise2;
}
}
resolvePromise的实现
- 处理promise的返回值
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2)
return reject(new TypeError("Chaining cycle detected for promise"));
// 我如何知道x是不是promise
if ((typeof x === "object" && x !== null) || typeof x === "function") {
// 有可能是promise
let called = false;
try {
let then = x.then; // then方法可能是通过defineProperty来进行定义的
if (typeof then === "function") {
// 是promise {then:function(){}}
then.call(x, (y) => {
// 为了防止promise解析后的结果依旧是promise,所以需要递归解析
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, (r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 就是一个对象或者函数 {a:1} function(){}
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x); // 普通值 直接将结果传递到下面就可以了
}
}
resolve和reject的实现
Promise.resolve(value) {
return new Promise((resolve, reject) => {
resolve(value);
});
}
Promise.reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}
all的实现
- 全部成功则返回
Promise.all = (callbacks) => {
return new Promise((resolve, reject) => {
let result = []
let index = 0
callbacks.forEach((item, index) => {
Promise.resolve(item).then(value => {
result[index] = value // 存储结果
if (++index === callbacks.length) {
resolve(result)
}
// 失败直接reject
}, reject)
})
})
}
finally的实现
- 无论成功和失败都要执行的逻辑
// 实现
Promise.prototype.finally = (fn) => {
return this.then((val) => {
return Promise.resolve(fn()).then(() => val)
}, (err) => {
return Promise.resolve(fn()).then(() => { throw err })
})
}
// 使用
Promise.resolve('abc').finally(() => { // 无论成功还是失败都会执行
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('abcasdasda')
}, 1000)
})
}).then((value) => {
console.log('成功', value)
}).catch((value) => {
console.log('失败', value)
})
allSettled
- 不管失败/成功的请求全部返回
race的实现
- 返回第一个成功的promise
Promise.race = (values) => {
return new Promise((resolve, reject) => {
this.values.forEach((item) => {
Promise.resolve(item).then(resolve, reject)
})
})
}
generator(生成器)
- async + await 前身 generator
- generator 是一个语法 , 可以让我们的异步代码 写的更像同步
const fs = require("fs/promises");
const path = require('path')
function* readFile() {
let data1 = yield fs.readFile(path.resolve(__dirname, "a.txt"), "utf8");
let data2 = yield fs.readFile(path.resolve(__dirname, data1), "utf8");
return data2; // 30
}
function co(iter) {
return new Promise((resolve, reject) => {
const step = (data) => {
const { value, done } = iter.next(data)
if (done) { // 完成返回值
resolve(value)
return
}
Promise.resolve(value).then(res => {
step(data)
}).catch(err => {
reject(err)
})
}
step()
})
}
co(readFile()).then(data => {
console.log(data);
}).catch(e => {
console.log(e);
})
async + await
- ( generator + co) 语法糖
- async函数返回的就是一个promise co(readFile())
const fs = require("fs/promises");
const path = require('path')
async function readFile() {
try {
let data1 = await fs.readFile(path.resolve(__dirname, "a.txt"), "utf8");
let data2 = await fs.readFile(path.resolve(__dirname, data1), "utf8");
return data2; // 30
} catch (e) {
console.log(e)
}
}
readFile().then(data=>{
console.log(data)
})
扩展:进程和线程
- 进程是系统进行分配资源的最小单位,计算机是以进程来分配任务和调度任务
- 每个进程都是独立的,但是可以通信(通过ipc)
- 进程中包含着线程
- 浏览器是由多个进程组成的,浏览器中主要的进程有:
- 主进程
- 网络进程
- 绘图的进程
- 插件都是一个个的进程
- 每个页签都是一个独立的进程 (渲染进程): 渲染的线程
- 渲染进程中 : 进程中包含的线程
- ui线程 负责页面渲染、布局、绘制
- js线程:js引擎线程, 主要负责执行js代码的 (ui线程 js线程互斥的,js也是单线程的) 为了保证渲的一致性要保证整个执行是单线程的
- 主线程:单线程的 (代码是从上大下执行) webworker(不能操作dom元素) 同步代码
- 定时器、请求、用户的事件操作 都是异步的 (每次开一个定时器 都会生成一个新的线程)
异步任务的划分 : 宏任务、
- 微任务:
- Promise.then(ECMAScript里面提供的)
- mutationObserver(html5,这里的回调是一异步执行的)
- queueMircrotask
- 宏任务:
- 默认的script脚本 ui渲染也是一个宏任务
- setTimout
- 网络请求
- 用户的事件、
- messageChannel
- setImmediate
- requestIDleCallback requestFrameAnimation (这个是放在渲染的) 只能算回调,你可以当成宏任务
- 代码执行的过程中
- 宏任务和微任务 会将对应的结果放到不同的队列中
- 等待当前宏任务执行完毕后,会将微任务全部清空 (在微任务执行的过程中 如果在产生微任务 则会将当前产生的微任务放到队列尾部 )
- 微任务都执行完毕后,会在宏任务队列中拿出来一个执行 (宏任务每次执行一个,微任务每次执行一堆)
- js 是单线程的所以要有一个事件触发线程 来实现任务的调度
- 宏任务的执行顺序 是按照 调用的顺序 (时间一样的情况下), 如果时间不一样,则以放入的顺序为准
- 渲染是要在特定的时机才能渲染, 根据浏览器的刷新频率 16.6ms, 不是每一轮都要渲染的 (一定在微任务之后)