近日,整理了学习笔记,而后分享给你们,共同窗习,共同进步,篇幅过长建议收藏。css
js异步编程咱们从如下几个部分来看一看html
首先咱们得清楚的知道,什么是同步,什么是异步?前端
通俗的来说,同步指的是 调用以后获得的结果,才能够去作别的任务node
而异步指的是 调用以后先无论结果,继续再干别的任务git
在深一步了解异步相关内容以前咱们先来了解两个重要的概念。github
进程是程序运行的实例,同一个程序能够产生多个进程,一个进程有能够包含一个或者多个线程web
线程是操做系统可以运算调度的最小单位,一次只能执行一个任务,有本身的调用栈,寄存器环境,同一进程的线程共享进程资源。ajax
那么,问题就又来了,咱们知道JS是单线程的,那么单线程的JS又是如何实现一步的呢?chrome
答案呢,其实就是单线程的JS是经过浏览器内核多线程实现异步编程
咱们来深刻的了解一下浏览器相关工做原理吧。
以开源的Chromium为例
浏览器的进程
渲染进程又包含
渲染布局(页面的html,css,js,构建DOM树和渲染树就是GUI线程的工做)
解析、执行JS程序(chrome v8引擎),JS引擎线程只有一个,这也是为何咱们所说的js是单线程的缘由,其实呢语言是没有单线程多线程之说的,由于解释这个语言的引擎是单线程的,因此咱们说js是单线程的
JS引擎线程与GUI线程互斥,由于JS引擎线程也能够操做DOM,容易形成混乱
尽可能控制js文件的大小,不要让js执行时间太长
setTimeout
setInterval
将知足触发条件的事件放入任务队列(异步事件放入任务队列)
处理ajax请求的线程
XHR所在线程
若是请求完成时有回调函数,它就会通知事件触发线程往任务队列里面添加事件
知道了浏览器是如何进行工做的以后,咱们回顾一下前端常见的有哪些异步场景?
Event Loop
顺序以下:
1.调用webApi(setTimeout)
2.定时器线程计数2s
3.事件触发线程将定时器事件放入任务队列(往执行栈中加任务)
4.主线程经过EventLoop遍历任务队列(往执行栈中出任务)
复制代码
// for 循环是同步任务,因此等for 执行完以后才会去执行定时器这个异步任务, var做用域是全局的,没有块级做用域
for (var i = 1; i <= 10; i ++) {
setTimeout(function() {
console.log(i)
}, 1000 * i)
}
复制代码
// 利用函数闭包构建做用域
for (var i = 1; i <= 10; i ++) {
(function(i) {
setTimeout(function() {
console.log(i)
}, 1000 * i)
})(i)
}
复制代码
// ES6新增的let 块级做用域
for (let i = 1; i <= 10; i ++) {
setTimeout(function() {
console.log(i)
}, 1000 * i)
}
复制代码
主要根据在浏览器环境下的Event Loop来讲明,node.js的EventLoop 后续node部分会讲到。
首先,咱们要明白异步是怎么去实现的:
异步的任务有两类
Event Loop执行顺序
注意
一个Event Loop 有一个或多个task queue
每一个Event Loop 有一个 Microtask queue
首先呢,咱们要先去知道,异步编程的方法有哪些?
咱们一个一个的来说述,回调函数你们很经常使用,直接开始事件发布/订阅这个异步编程方法。
那么如何理解发布/订阅呢?
有三个核心概念咱们得了解一下
1. 首先发布者 发布消息到 事件中心
2. 而后订阅者 从事件中心 订阅消息
3. 订阅者能够有多个
复制代码
如何实现一个事件的发布/订阅呢?
class PubSub {
constructor() {
// 用对象来存储,是由于事件的名字和事件的处理函数用对象能够很方便的对应起来
this.events = {}
}
publish(eventName, data) {
if (this.events[eventName]) {
this.events[eventName].forEach(cb => {
cb.apply(this, data)
})
}
}
subscribe(eventName, cb) {
if (this.events[eventName]) {
this.events[eventName].push(cb)
} else {
this.events[eventName] = [cb]
}
}
// 取消订阅
unSubscribe(eventName, cb) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(item => item !== cb)
}
}
}
复制代码
优势
缺点
咱们先来看一看promise/A+ 规范。
一个有then方法的对象或者函数,行为符合本规范
一个定义了then方法的对象或函数
任何JavaScript的合法值
throw语句抛出的值
表示一个promise的拒绝缘由
state: pending
1. value --> state: fulfilled
2. reason --> state: rejected
复制代码
一个promise必须提供一个then方法来访问其当前值、终值和拒因
promise的then方法接受两个参数
const promise2 = promise1.then(onFulfilled, onRejected)
复制代码
onFulfilled 和 onRejected都是可选参数
若是onFulfilled不是函数,其必须被忽略
若是onRejected不是函数,其必须被忽略
onFulfilled 不是函数,promise1的状态是fulfilled
state:fulfilled
value:同promise1
onFulfilled 不是函数,promise1的状态是rejected
state:rejected
value:同promise1
onFulfilled或者onRejected 是一个函数
return x
进入解析过程
复制代码
若是onFulfilled是函数:
当promise执行结束后其必须被调用,其第一个参数为promise的终值
在promise执行结束前其不可被调用
其调用次数不可超过一次
若是onRejected是函数:
当promise被拒绝执行后其必须被调用,其第一个参数为promise的拒因
在promise被拒绝执行前其不可被调用
其调用次数不可超过一次
then方法能够被同一个promise调用屡次
promise
成功执行时,全部 onFulfilled
需按照其注册顺序依次回调promise
被拒绝执行时,全部的 onRejected
需按照其注册顺序依次回调const promise2 = promise1.then(onFulfilled, onRejected)
复制代码
onFulfilled 不是函数,promise1的状态是fulfilled
state:fulfilled
value:同promise1
onFulfilled 不是函数,promise1的状态是rejected
state:rejected
value:同promise1
onFulfilled或者onRejected 是一个函数
return x
进入解析过程
复制代码
先抽象出一个模型resolove(promise, x)
若是 promise
和 x
指向同一对象,以 TypeError
为据因拒绝执行 promise
若是 x
为 Promise ,则使 promise
接受 x
的状态
x
处于等待态, promise
需保持为等待态直至 x
被执行或拒绝x
处于执行态,用相同的值执行 promise
x
处于拒绝态,用相同的据因拒绝 promise
若是 x
为对象或者函数:
把 x.then
赋值给 then
若是取 x.then
的值时抛出错误 e
,则以 e
为据因拒绝 promise
若是then
是函数,将x
做为函数的做用域this
调用之。传递两个回调函数做为参数,第一个参数叫作resolvePromise
,第二个参数叫作rejectPromise
:
resolvePromise
以值 y
为参数被调用,则运行 [[Resolve]](promise, y)
rejectPromise
以据因 r
为参数被调用,则以据因 r
拒绝 promise
resolvePromise
和 rejectPromise
均被调用,或者被同一参数调用了屡次,则优先采用首次调用并忽略剩下的调用若是调用then
方法方法抛出了异常e
resolvePromise
或 rejectPromise
已经被调用,则忽略之e
为据因拒绝 promise
若是 then
不是函数,以 x
为参数执行 promise
若是 x
不为对象或者函数,以 x
为参数执行 promise
那么咱们来实现如下这个解析过程吧
import { isObject, isFunction } from "util"
//! promise 解析过程
function resolve(promise, x) {
if (x === promise) {
return reject(promise, new TypeError('cant be the same'))
}
if (isPromise(x)) {
if (x.state === 'pending') {
return x.then(() => {
resolve(promise, x.value)
}, () => {
reject(promise, x.value)
})
}
if (x.state === 'fulfilled') {
return fulfill(promise, x.value)
}
if (x.state === 'rejected') {
return reject(promise, x.value)
}
} else if (isObject(x) || isFunction(x)) {
let then;
try {
then = x.then
} catch (e) {
return reject(promise, e)
}
if (isFunction(then)) {
let isCalled = false;
try {
then.call(x, function resolvePromise(y) {
if (isCalled) {
return
}
isCalled = true
resolve(promise, y)
}, function rejectPromise(r) {
if (isCalled) {
return
}
isCalled = true
reject(promise, r)
})
} catch (e) {
if (!isCalled) {
reject(promise, e)
}
}
} else {
return fulfill(promise, x)
}
} else {
return fulfill(promise, x)
}
}
复制代码
接下来咱们来看一下ES6 Promise API
构造函数 | 说明 |
---|---|
new Promise(function(resolve, reject) { }) | 函数做为参数 |
resolve函数将promise状态从pending变成resolved(fulfilled) | |
reject函数将promise状态从pending变成rejected |
方法 | 说明 |
---|---|
Promise.resolve(param) | 等同于 new Promise(function(resolve, reject){resolve(param)}) |
Promise.reject(reason) | 等同于 new Promise(function(resolve, reject){reject(reason)}) |
Promise.all([p1,...,pn]) | 输入一组promise返回一个新的promise,所有promise都是fulfilled结果才是fulfilled状态 |
Promise.allSettled([p1,...,pn]) | 输入一组promise返回一个新的promise,全部的promise都是fulfilled结果才是fulfilled状态 |
Promise.race([p1,...,pn]) | 输入一组promise返回一个新的promise,结果promise的状态根据第一个变化的promise状态 |
方法 | 说明 |
---|---|
promise.then(onFulfilled,onRejected) | promise 状态改变以后的回调,返回新的promise对象 |
promise.catch(function(reason) {}) | 同promise.then(null, onRejected),promise状态为rejected的回调 |
promise.finally(function(reason) { // test}) | 同promise.then(function(){ // test}, function(){ // test}),无论promise状态如何都会执行 |
注意点
因此说写catch的时候只须要在链式调用的最后面加一个catch语句去捕获就能够。
**题目:**3秒以后亮一次红灯,再过两秒亮一次绿灯,再过一秒亮一次黄灯,用promise 实现屡次交替亮灯的效果,console.log 模拟亮灯
// 思路拆解:
// 1.多少秒后亮某个颜色的灯
// 2.顺序亮一批灯
// 3.循环顺序亮一批灯
function light(color, second) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log(color)
resolve()
}, second * 1000)
})
}
function orderLights(list) {
let promise = Promise.resolve()
list.forEach(item => {
promise = promise.then(() => {
return light(item.color, item.second)
})
})
promise.then(function() {
return orderLights(list)
})
}
const list = [
{color: 'red', second: 3},
{color: 'green', second: 2},
{color: 'yellow', second: 1},
]
orderLights(list)
复制代码
首先先来看两个概念
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = i >= items.length
var value = !done ? items[i++] : undefined
return {
done,
value
}
}
}
}
复制代码
Generator函数(生成器)
执行Generator函数生成一个生成器对象
只能出如今Generator函数里
用来暂停和恢复生成器函数
next执行
next参数
function* createGenerator() {
let first = yield 1
let second = yield first + 2
yield second + 3
}
let gen = createGenerator();
let g1 = gen.next() // {value: 1, done: false}
let g2 = gen.next(4) // {value: 6, done: false}
let g3 = gen.next(5) // {value: 8, done: false}
let g4 = gen.next() // {value: undefined, done: true}
复制代码
yield生成器函数 / 可迭代对象
举个栗子
function* generator1() {
yield 1;
yield 2;
}
function* generator2() {
yield 100;
yield* generator1();
yield 200;
}
let g = generator2()
g.next() //? { value: 100, done: false }
g.next() //? { value: 1, done: false }
g.next() //? { value: 2, done: false }
g.next() //? { value: 200, done: false }
g.next() //? { value: undefined, done: true }
复制代码
return(param)
//! return(param)
function* createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
iterator.next(); // { value: 1, done: false }
iterator.return();// { value: undefined, done: true }
iterator.next();// { value: undefined, done: true }
复制代码
throw(param)
//! throw(param)
function* createIterator() {
let first = yield 1;
let second;
try{
second = yield first + 2;
} catch (e) {
second = 6;
}
yield second + 3
}
let iterator = createIterator();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next(10)); // { value: 12, done: false }
console.log(iterator.throw(new Error('error'))); // { value: 9, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
复制代码
接下来咱们来看一下generator函数的实现原理
首先先得了解找一个概念
耦合程度仍是挺高的
举个栗子
const fs = require('fs');
const Thunk = function(fn) {
return function(...args) {
return function(callback) {
return fn.call(this, ...args, callback)
}
}
}
const readFileThunk = Thunk(fs.readFile);
function run(fn) {
var gen = fn();
function next(err, data) {
var result =gen.next(data);
if (result.done) return;
result.value(next);
}
next()
}
const g = function*() {
const s1 = yield readFileThunk('./g1.json')
console.log(s1.toString());
const s2 = yield readFileThunk('./g2.json')
console.log(s2.toString());
const s3 = yield readFileThunk('./g3.json')
console.log(s3.toString());
}
run(g);
复制代码
co模块是generator函数的自动执行器,功能相似与Thunk函数做用
co源码:github.com/tj/co
async function test() {
return 1;
}
const p = test();
console.log(p); // Promise { 1 }
p.then(function (data) {
console.log(data) // 1
})
async function test() {
throw new Error('error')
}
const p = test();
console.log(p);
p.catch(function (data) {
console.log(data)
})
复制代码
await
1. promise
1> resolved 返回promise的值
2> rejected 抛出promise的拒因
2. 非promise
1> 返回对应的值 await 1
复制代码
Generator + 自动执行器
// async函数实现原理
async function example(params) {
// ...
}
function example(params) {
return spawn(function*() {
// ...
})
}
function spawn(genF) {
return new Promise(function(resolve, reject) {
const gen = genF(); // 生成器对象
function step(nextF) {
let next;
try {
next = nextF(); //执行gen.next()
} catch (e) {
return reject(e)
}
if (next.done) {
return resolve(next.value)
}
//* next.done 为 false时,继续step;
Promise.resolve(next.value).then(
function(v) {
step(function() {
return gen.next(v)
})
},
function(e) {
stop(function() {
return gen.throw(e)
})
}
)
}
step(function() {
return gen.next(undefined)
})
})
}
复制代码
整理的过程当中,不免会有疏漏,如果看到有误或者须要补充的知识点,欢迎留言小编