关注公众号“ 执鸢者”,回复“ 书籍”获取大量前端学习资料,回复“ 前端视频”获取大量前端教学视频,回复“ 异步”获取本节总体思惟导图。
本节主要阐述六种异步方案:回调函数、事件监听、发布/订阅、Promise、Generator和Async。其中重点是发布/订阅、Promise、Async的原理实现,经过对这几点的了解,但愿咱们前端切图仔可以在修炼内功的路上更进一步。
异步编程的最基本方法,把任务的第二段单独写在一个函数里面,等到从新执行这个任务的时候,就直接调用这个函数。
function sleep(time, callback) { setTimeout(() => { // 一些逻辑代码 callback(); }, time); }
异步任务的执行不取决于代码的执行顺序,而取决于某个事件是否发生。
dom.addEventListener('click', () => { console.log('dom被点击后触发!!!'); })
发布/订阅模式在观察者模式的基础上,在目标和观察者之间增长一个调度中心。订阅者(观察者)把本身想要订阅的事件注册到调度中心,当该事件触发的时候,发布者(目标)发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码。
Promise 是异步编程的一种解决方案,是为解决回调函数地狱这个问题而提出的,它不是新的语法功能,而是一种新的写法,容许将回调函数的嵌套改成链式调用。
const promise = new Promise((resolve, reject) => { if (/*若是异步成功*/) { resolve(value); } else { reject(error); } }); promise.then((value) => { // ...success }, (reason) => { // ...failure })
Generator 函数是ES6提供的一种异步编程解决方案,语法行为与传统函数彻底不一样。其最大特色是能够控制函数的执行。
function* genF() { yield 'come on!'; yield 'Front End Engineer'; return 'goood'; } const gF = genF(); gF.next();// {value: "come on!", done: false} gF.next();// {value: "Front End Engineer", done: false} gF.next();// {value: "goood", done: true} gF.next();// {value: undefined, done: true}
ES2017 标准引入了async函数,使得异步操做变得更加方便。简言之,该函数就是Generator函数的语法糖。
async function asyncFun() { await func1() await func2(); return '666'; } function func1() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('888') }, 100); }).then((value) => { console.log(value); }); } function func2() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('777') }); }).then((value) => { console.log(value); }); } asyncFun().then((value) => { console.log(value); }); // 888 // 777 // 666
不论是实际开发中仍是面试过程当中,各位老铁们对Promise确定不会陌生,下面就让咱们一块儿来唠一唠Promsie的实现原理,根据PromiseA+规范来进行实现,而后对其相关的静态方法(Promise.resolve()、Promise.reject()、Promise.all()、Promise.race())和实例方法(Promise.prototype.catch()、Promise.prototype.finally())进行实现。
首先用一幅图来展现一下我考虑实现这个函数的思路吧。
人家有相关标准,咱们就要遵照,毕竟遵纪守法才是好公民,如今只能硬着头皮把这个标准过一遍。
下面就是基于Promise/A+规范实现的代码,已经通过promises-aplus-tests库进行了验证。
const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; /** * Promise构造函数 * excutor: 内部同步执行的函数 */ class Promise { constructor(excutor) { const self = this; self.status = PENDING; self.onFulfilled = [];// 成功的回调 self.onRejected = [];// 失败的回调 // 异步处理成功调用的函数 // PromiseA+ 2.1 状态只能由Pending转为fulfilled或rejected;fulfilled状态必须有一个value值;rejected状态必须有一个reason值。 function resolve(value) { if (self.status === PENDING) { self.status = FULFILLED; self.value = value; // PromiseA+ 2.2.6.1 相同promise的then能够被调用屡次,当promise变为fulfilled状态,所有的onFulfilled回调按照原始调用then的顺序执行 self.onFulfilled.forEach(fn => fn()); } } function reject(reason) { if (self.status === PENDING) { self.status = REJECTED; self.reason = reason; // PromiseA+ 2.2.6.2 相同promise的then能够被调用屡次,当promise变为rejected状态,所有的onRejected回调按照原始调用then的顺序执行 self.onRejected.forEach(fn => fn()); } } try { excutor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { // PromiseA+ 2.2.1 onFulfilled和onRejected是可选参数 // PromiseA+ 2.2.5 onFulfilled和onRejected必须被做为函数调用 // PromiseA+ 2.2.7.3 若是onFulfilled不是函数且promise1状态是fulfilled,则promise2有相同的值且也是fulfilled状态 // PromiseA+ 2.2.7.4 若是onRejected不是函数且promise1状态是rejected,则promise2有相同的值且也是rejected状态 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; const self = this; const promise = new Promise((resolve, reject) => { const handle = (callback, data) => { // PromiseA+ 2.2.4 onFulfilled或者onRejected须要在本身的执行上下文栈里被调用,因此此处用setTimeout setTimeout(() => { try { // PromiseA+ 2.2.2 若是onFulfilled是函数,则在fulfilled状态以后调用,第一个参数为value // PromiseA+ 2.2.3 若是onRejected是函数,则在rejected状态以后调用,第一个参数为reason const x = callback(data); // PromiseA+ 2.2.7.1 若是onFulfilled或onRejected返回一个x值,运行这[[Resolve]](promise2, x) resolvePromise(promise, x, resolve, reject); } catch (e) { // PromiseA+ 2.2.7.2 onFulfilled或onRejected抛出一个异常e,promise2必须以e的理由失败 reject(e); } }) } if (self.status === PENDING) { self.onFulfilled.push(() => { handle(onFulfilled, self.value); }); self.onRejected.push(() => { handle(onRejected, self.reason); }) } else if (self.status === FULFILLED) { setTimeout(() => { handle(onFulfilled, self.value); }) } else if (self.status === REJECTED) { setTimeout(() => { handle(onRejected, self.reason); }) } }) return promise; } } function resolvePromise(promise, x, resolve, reject) { // PromiseA+ 2.3.1 若是promise和x引用同一对象,会以TypeError错误reject promise if (promise === x) { reject(new TypeError('Chaining Cycle')); } if (x && typeof x === 'object' || typeof x === 'function') { // PromiseA+ 2.3.3.3.3 若是resolvePromise和rejectPromise都被调用,或者对同一个参数进行屡次调用,那么第一次调用优先,之后的调用都会被忽略。 let used; try { // PromiseA+ 2.3.3.1 let then be x.then // PromiseA+ 2.3.2 调用then方法已经包含了该条(该条是x是promise的处理)。 let then = x.then; if (typeof then === 'function') { // PromiseA+ 2.3.3.3若是then是一个函数,用x做为this调用它。第一个参数是resolvePromise,第二个参数是rejectPromise // PromiseA+ 2.3.3.3.1 若是resolvePromise用一个值y调用,运行[[Resolve]](promise, y) // PromiseA+ 2.3.3.3.2 若是rejectPromise用一个缘由r调用,用r拒绝promise。 then.call(x, (y) => { if (used) return; used = true; resolvePromise(promise, y, resolve, reject) }, (r) => { if (used) return; used = true; reject(r); }) } else { // PromiseA+ 若是then不是一个函数,变为fulfilled状态并传值为x if (used) return; used = true; resolve(x); } } catch (e) { // PromiseA+ 2.3.3.2 若是检索属性x.then抛出异常e,则以e为缘由拒绝promise // PromiseA+ 2.3.3.4 若是调用then抛出异常,可是resolvePromise或rejectPromise已经执行,则忽略它 if (used) return; used = true; reject(e); } } else { // PromiseA+ 2.3.4 若是x不是一个对象或函数,状态变为fulfilled并传值x resolve(x); } }
按照Promise/A+规范实现了Promise的核心内容,可是其只实现了Promise.prototype.then()方法,那其它方法呢?下面咱们就唠一唠其它方法,包括静态方法(Promise.resolve()、Promise.reject()、Promise.all()、Promise.race())和实例方法(Promise.prototype.catch()、Promise.prototype.finally())。
class Promise { // ... // 将现有对象转为 Promise 对象 static resolve(value) { // 若是参数是 Promise 实例,那么Promise.resolve将不作任何修改、原封不动地返回这个实例。 if (value instanceof Promise) return value; // 参数是一个thenable对象(具备then方法的对象),Promise.resolve方法会将这个对象转为 Promise 对象,而后就当即执行thenable对象的then方法。 if (typeof value === 'object' || typeof value === 'function') { try { let then = value.then; if (typeof then === 'function') { return new Promise(then.bind(value)); } } catch (e) { return new Promise((resolve, reject) => { reject(e); }) } } // 参数不是具备then方法的对象,或根本就不是对象,Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。 return new Promise((resolve, reject) => { resolve(value); }) } }
class Promise { // ... // 返回一个新的 Promise 实例,该实例的状态为rejected。 static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } }
class Promise { // ... // 用于将多个 Promise 实例,包装成一个新的 Promise 实例。只有全部状态都变为fulfilled,p的状态才会是fulfilled static all(promises) { const values = []; let resolvedCount = 0; return new Promise((resolve, reject) => { promises.forEach((p, index) => { Promise.resolve(p).then(value => { resolvedCount++; values[index] = value; if (resolvedCount === promises.length) { resolve(values); } }, reason => { reject(reason); }) }) }) } }
class Promise { // ... // 只要有一个实例率先改变状态,状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给回调函数。 static race(promises) { return new Promise((resolve, reject) => { promises.forEach((p, index) => { Promise.resolve(p).then(value => { resolve(value); }, reason => { reject(reason); }) }) }) } }
class Promise { // ... // 是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。 catch(onRejected) { return this.then(undefined, onRejected); } }
class Promise { // ... // 用于指定无论 Promise 对象最后状态如何,都会执行的操做。 finally(callback) { return this.then( value => Promise.resolve(callback()).then(() => value), reason => Promise.resolve(callback()).then(() => { throw reason }) ) } }
在开发过程当中经常使用的另外一种异步方案莫过于Async,经过async函数的引入使得异步操做变得更加方便。实质上,async是Generator的语法糖,最大的亮点是async内置执行器,调用后便可自动执行,不像Generator须要调用next()方法才能执行。
这是Async的实现原理,即将Generator函数做为参数放入run函数中,最终实现自动执行并返回Promise对象。
function run(genF) { // 返回值是Promise return new Promise((resolve, reject) => { const gen = genF(); function step(nextF) { let next; try { // 执行该函数,获取一个有着value和done两个属性的对象 next = nextF(); } catch (e) { // 出现异常则将该Promise变为rejected状态 reject(e); } // 判断是否到达末尾,Generator函数到达末尾则将该Promise变为fulfilled状态 if (next.done) { return resolve(next.value); } // 没到达末尾,则利用Promise封装该value,直到执行完毕,反复调用step函数,实现自动执行 Promise.resolve(next.value).then((v) => { step(() => gen.next(v)) }, (e) => { step(() => gen.throw(e)) }) } step(() => gen.next(undefined)); }) }
更加详细内容能够参考《图解23种设计模式》前端
发布/订阅模式在观察者模式的基础上,在目标和观察者之间增长一个调度中心。订阅者(观察者)把本身想要订阅的事件注册到调度中心,当该事件触发的时候,发布者(目标)发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码。
// 发布订阅(TypeScript版) interface Publish { registerObserver(eventType : string, subscribe : Subscribe) : void; remove(eventType : string, subscribe ?: Subscribe) : void; notifyObservers(eventType : string) : void; } interface SubscribesObject{ [key : string] : Array<Subscribe> } class ConcretePublish implements Publish { private subscribes : SubscribesObject; constructor() { this.subscribes = {}; } registerObserver(eventType : string, subscribe : Subscribe) : void { if (!this.subscribes[eventType]) { this.subscribes[eventType] = []; } this.subscribes[eventType].push(subscribe); } remove(eventType : string, subscribe ?: Subscribe) : void { const subscribeArray = this.subscribes[eventType]; if (subscribeArray) { if (!subscribe) { delete this.subscribes[eventType]; } else { for (let i = 0; i < subscribeArray.length; i++) { if (subscribe === subscribeArray[i]) { subscribeArray.splice(i, 1); } } } } } notifyObservers(eventType : string, ...args : any[]) : void { const subscribes = this.subscribes[eventType]; if (subscribes) { subscribes.forEach(subscribe => subscribe.update(...args)) } } } interface Subscribe { update(...value : any[]) : void; } class ConcreteSubscribe1 implements Subscribe { public update(...value : any[]) : void { console.log('已经执行更新操做1,值为', ...value); } } class ConcreteSubscribe2 implements Subscribe { public update(...value : any[]) : void { console.log('已经执行更新操做2,值为', ...value); } } function main() { const publish = new ConcretePublish(); const subscribe1 = new ConcreteSubscribe1(); const subscribe2 = new ConcreteSubscribe2(); publish.registerObserver('1', subscribe1); publish.registerObserver('2', subscribe2); publish.notifyObservers('2', '22222'); } main();
相关章节
图解JavaScript——代码实现【1】
图解JavaScript————基础篇
图解JavaScript————进阶篇
图解23种设计模式(TypeScript版)参考连接
Prmose/A+
Promise源码实现
ES6入门教程es6欢迎你们关注公众号(回复“异步”获取本节的思惟导图,回复“书籍”获取大量前端学习资料,回复“前端视频”获取大量前端教学视频)
面试