上篇博客写着写着没动力,而后就拖了一个月。javascript
如今打算在一周内完成。html
这篇讲Promise
和co
的原理+实现。java
Promise的规范有不少,其中ECMAScript 6
采用的是Promises/A+.
想要了解更多最好仔细读完Promises/A+
,顺便说下Promise
是依赖于异步实现。node
而在JavaScript
中有两种异步宏任务macro-task
和微任务micro-task
.react
在挂起任务时,JS 引擎会将全部任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫作 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的全部任务顺序执行;以后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。git
常见的异步代码实现github
macro-task
: script(总体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering面试
micro-task
: process.nextTick, Promises(原生 Promise), Object.observe(api已废弃), MutationObservertypescript
以上的知识摘查于Promises/A+
顺便说下一个前段时间看到的一个js
面试题api
setTimeout(function() { console.log(1) }, 0); new Promise(function executor(resolve) { console.log(2); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(3); }).then(function() { console.log(4); }); console.log(5);
在node v8.13使用原生的Promise
是: "2 3 5 4 1"。
可是若是使用bluebird
的第三方Promise
就是: "2 3 5 1 4"。
这个缘由是由于bluebird
在这个环境下优先使用setImmediate
代码。
而后再看上面的代码执行顺序.
第一次总体代码进入macro-task
。micro-task
为空。
macro-task
执行总体代码,setTimeout
加入下一次的macro-task
。Promise
执行打出2 3
,then
加入micro-task
, 最后打出5
。
micro-task
执行then
被执行因此打出4
。
从新执行macro-task
因此打出1
可是在bluebird
里的then
使用setImmediate
因此上面的步骤会变成:
步骤2then
在setTimeout
后加入macro-task
。
步骤3会由于micro-task
为空跳过。
步骤4 执行setTimeout
,then
打出1 4
。
new Promise(func:(resolve, reject)=> void 0), 这里的func方法被同步执行。
Promise
会有三种状态PENDING(执行)
,FULFILLED(执行成功)
,REJECTED(执行失败)
。
在resolve
,reject
均未调用且未发生异常时状态为PENDING
。
resolve
调用为FULFILLED
,reject
调用或者发生异常为REJECTED
。
在给Promise
实例调用then(callFulfilled, callRejected)
来设置回调,状态不为PENDING
时会根据状态调用callFulfilled
和callRejected
。
then
须要返回一个新的Promise
实例.
状态为PENDING
则会把callFulfilled
和callRejected
放入当前Promise
实例的回调队列中,队列还会存储新的Promise
实例。
在状态改变为FULFILLED
或REJECTED
时会回调当前Promise
实例的队列。
Promise Api
下面是Promise
的全部开放api这里为了区分与原生的因此类名叫Appoint
。
顺便为了学习typescript
class Appoint { public constructor(resolver: Function){}; public then(onFulfilled, onRejected): Appoint {}; public catch(onRejected): Appoint {}; public static resolve(value): Appoint {}; public static reject(error): Appoint {}; public static all(iterable: Appoint[]): Appoint {}; public static race(iterable): Appoint {}; }
function INTERNAL() {} enum AppointState { PENDING, FULFILLED, REJECTED, } class Appoint { public handled: boolean; public value: any; public queue: QueueItem[]; private state: AppointState; public constructor(resolver: Function){ if (!isFunction(resolver)) { throw new TypeError("resolver must be a function"); } // 设置当前实例状态 this.state = AppointState.PENDING; this.value = void 0; // 初始化回调队列 this.queue = []; // true表明没有设置then this.handled = true; if (resolver !== INTERNAL) { // 安全执行传入的函数 safelyResolveThen(this, resolver); } } // state 的getset public setState(state: AppointState) { if (this.state === AppointState.PENDING && this.state !== state) { this.state = state; } } public getState(): AppointState { return this.state; } }
function safelyResolveThen(self: Appoint, then: (arg: any) => any) { let called: boolean = false; try { then(function resolvePromise(value: any) { if (called) { return; } // 保证doResolve,doReject只执行一次 called = true; // 改变当前状态以及调用回调队列 doResolve(self, value); }, function rejectPromise(error: Error) { if (called) { return; } // 同上 called = true; doReject(self, error); }); } catch (error) { // 特别捕捉错误 if (called) { return; } called = true; doReject(self, error); } }
/** * 若是value不是一个Promise,对Promise调用回调队列。 * 若是是就等待这个Promise回调 */ function doResolve(self: Appoint, value: any) { try { // 判断是否为Promise const then = getThen(value); if (then) { // safelyResolveThen(self, then); } else { // 改变状态 self.setState(AppointState.FULFILLED); self.value = value; // 调用回调队列 self.queue.forEach((queueItem) => { queueItem.callFulfilled(value); }); } return self; } catch (error) { return doReject(self, error); } } /** * 调用回调队列 */ function doReject(self: Appoint, error: Error) { // 改变状态 self.setState(AppointState.REJECTED); self.value = error; if (self.handled) { // 未设置then回调 asap(() => { // 建立一个异步任务保证代码都执行了再判断 if (self.handled) { if (typeof process !== "undefined") { // node 环境下触发unhandledRejection事件 process.emit("unhandledRejection", error, self); } else { // 浏览器环境直接打印便可 console.error(error); } } }); } self.queue.forEach((queueItem) => { queueItem.callRejected(error); }); return self; } /** * 判断是否为Object且有then属性的方法, * 有返回这个方法的绑定this * 这种判断方式会发生若是 * resolve({ then: () => {} })的话就会丢失下次的then * 原生的Promise也是相同 */ function getThen(obj: any): Function { const then = obj && obj.then; if (obj && (isObject(obj) || isFunction(obj)) && isFunction(then){ return then.bind(obj); } return null; }
/** * 使用micro-task的异步方案来执行方法 */ function asap(callback) { if (typeof process !== "undefined") { process.nextTick(callback); } else { const BrowserMutationObserver = window.MutationObserver || window.WebKitMutationObserver let iterations = 0; const observer = new BrowserMutationObserver(callback); const node: any = document.createTextNode(""); observer.observe(node, { characterData: true }); node.data = (iterations = ++iterations % 2); } } /** * 异步执行then,catch */ function unwrap(promise: Appoint, func: Function, value: any): void { asap(() => { let returnValue; try { // 执行then,catch回调得到返回值 returnValue = func(value); } catch (error) { // 发生异常直接触发该promise的Reject return doReject(promise, error); } if (returnValue === promise) { // 执行then,catch回调返回值不能为promise本身 doReject(promise, new TypeError("Cannot resolve promise with itself")); } else { // then,catch回调成功,直接触发该promise的Resolve doResolve(promise, returnValue); } }); } public then<U>( onFulfilled?: (value?: any) => U, onRejected?: (error?: any) => U, ): Appoint { // 直接无视来作到值穿透 if (!isFunction(onFulfilled) && this.state === AppointState.FULFILLED || !isFunction(onRejected) && this.state === AppointState.REJECTED ) { return this; } // 新建一个空的 const promise = new Appoint(INTERNAL); // 当前实例已经被设置then if (this.handled) { this.handled = false; } if (this.getState() !== AppointState.PENDING) { // 当前实例已经结束运行直接根据状态获取要回调的方法 const resolver = this.getState() === AppointState.FULFILLED ? onFulfilled : onRejected; // 异步执行resolver,若是成功会触发新实例的then,catch unwrap(promise, resolver, this.value); } else { // 若是Promise的任务还在继续就直接把生成一个QueueItem // 并设置好新的Promise实例 this.queue.push(new QueueItem(promise, onFulfilled, onRejected)); } // 返回新生成的 return promise; } public catch<U>(onRejected: (error?: any) => U): Appoint { return this.then(null, onRejected); }
export class QueueItem { // 每次then|catch生成的新实例 public promise: Appoint; // then回调 public callFulfilled: Function; // catch回调 public callRejected: Function; constructor(promise: Appoint, onFulfilled?: Function, onRejected?: Function { this.promise = promise; if (isFunction(onFulfilled)) { this.callFulfilled = function callFulfilled(value: any) { // 异步执行callFulfilled,后触发新实例的then,catch unwrap(this.promise, onFulfilled, value); }; } else { this.callFulfilled = function callFulfilled(value: any) { // 没有设置callFulfilled的话直接触发新实例的callFulfilled /* 例以下面这种代码一次catch可是没有then而下面代码中的then是catch返回的新实例 因此须要直接 new Promise(() => { }) .catch(() => {}) .then() */ doResolve(this.promise, value); }; } if (isFunction(onRejected)) { this.callRejected = function callRejected(error: Error) { // 异步执行callRejected,后会触发新实例的then,catch unwrap(this.promise, onRejected, error); }; } else { this.callRejected = function callRejected(error: Error) { // 没有设置callRejected的话直接触发新实例的callRejected doReject(this.promise, error); }; } } }
export function isFunction(func: any): boolean { return typeof func === "function"; } export function isObject(obj: any): boolean { return typeof obj === "object"; } export function isArray(arr: any): boolean { return Object.prototype.toString.call(arr) === "[object Array]"; }
public static resolve(value: any): Appoint { if (value instanceof Appoint) { return value; } return doResolve(new Appoint(INTERNAL), value); } public static reject(error: any): Appoint { if (error instanceof Appoint) { return error; } return doReject(new Appoint(INTERNAL), error); }
/** * 传入一个Promise数组生成新的Promise全部Promise执行完后回调 */ public static all(iterable: Appoint[]): Appoint { const self = this; if (!isArray(iterable)) { return this.reject(new TypeError("must be an array")); } const len = iterable.length; let called = false; if (!len) { return this.resolve([]); } const values = new Array(len); let i: number = -1; const promise = new Appoint(INTERNAL); while (++i < len) { allResolver(iterable[i], i); } return promise; function allResolver(value: Appoint, index: number) { self.resolve(value).then(resolveFromAll, (error: Error) => { if (!called) { called = true; doReject(promise, error); } }); function resolveFromAll(outValue: any) { values[index] = outValue; if (index === len - 1 && !called) { called = true; doResolve(promise, values); } } } } /** * 与all相似可是,只要一个Promise回调的就回调 */ public static race(iterable: Appoint[]): Appoint { const self = this; if (!isArray(iterable)) { return this.reject(new TypeError("must be an array")); } const len = iterable.length; let called = false; if (!len) { return this.resolve([]); } const values = new Array(len); let i: number = -1; const promise = new self(INTERNAL); while (++i < len) { resolver(iterable[i]); } return promise; function resolver(value: Appoint) { self.resolve(value).then((response: any) => { if (!called) { called = true; doResolve(promise, response); } }, (error: Error) => { if (!called) { called = true; doReject(promise, error); } }); } }
不使用co的话不停的then,和callback明显会很难受。
function callback (null, name) { console.log(name) } new Promise(function(resolve) { resolve('<h1>test</h1>') }).then(html => { setTimeout(function(){ callback('test' + html) }, 100) })
改用co异步代码感受和写同步代码同样。
const co = require("co") co(function *test() { const html = yield new Promise(function(resolve) { resolve('<h1>test</h1>') }) console.log('--------') const name = yield function (callback) { setTimeout(function(){ callback(null, 'test' + html) }, 100) } return name }).then(console.log)
这里不得不说下Generator了,直接看执行效果吧:
function *gen() { const a = yield 1 console.log('a: ', a) const b = yield 2 console.log('b: ', b) return 3 } const test = gen() test.next() // Object { value: 1, done: false } test.next(4) // a: 4\n Object { value: 2, done: false } test.next(5) // b: 5\n Object { value: 3, done: true }
很明显除了第一次next
的参数都会赋值到上一次的yield
的左边变量。
最后一次的next
返回的value
是return
的值,其它都是yield
右边的变量。
而co
就是经过不停的next
获取到支持的异步对象回调后把值放到下次的next
中从而达到效果。
const slice = Array.prototype.slice; const co: any = function co_(gen) { const ctx = this; const args = slice.call(arguments, 1); return new Promise(function _(resolve, reject) { // 把传入的方法执行一下并存下返回值 if (typeof gen === "function") { gen = gen.apply(ctx, args); } // 1. 传入的是一个方法经过上面的执行得到的返回值, // 若是不是一个有next方法的对象直接resolve出去 // 2. 传入的不是一个方法且不是一个next方法的对象直接resolve出去 if (!gen || typeof gen.next !== "function") { return resolve(gen); } // 执行,第一次next不须要值 onFulfilled(); /** * @param {Mixed} res * @return {null} */ function onFulfilled(res?: any) { let ret; try { // 获取next方法得到的对象,并把上一次的数据传递过去 ret = gen.next(res); } catch (e) { // generator 获取下一个yield值发生异常 return reject(e); } // 处理yield的值把它转换成promise并执行 next(ret); return null; } /** * @param {Error} err * @return {undefined} */ function onRejected(err) { let ret; try { // 把错误抛到generator里,而且接收下次的yield ret = gen.throw(err); } catch (e) { // generator 获取下一个yield值发生异常 return reject(e); } // 处理yield的值 next(ret); } function next(ret) { // generator执行完并把返回值resolve出去 if (ret.done) { return resolve(ret.value); } // 把value转换成Promise const value = toPromise(ctx, ret.value); if (value && isPromise(value)) { // 等待Promise执行 return value.then(onFulfilled, onRejected); } // yield的值不支持 return onRejected(new TypeError("You may only yield a function, promise," + " generator, array, or object, " + 'but the following object was passed: "' + String(ret.value) + '"')); } }); };
toPromise
function toPromise(ctx: any, obj: any) { if (!obj) { return obj; } if (isPromise(obj)) { return obj; } // 判断是 Generator 对象|方法 直接经过 co 转换为Promise if (isGeneratorFunction(obj) || isGenerator(obj)) { return co.call(ctx, obj); } // 判断是个回调方法 if ("function" === typeof obj) { return thunkToPromise(ctx, obj); } // 判断是个数组 if (Array.isArray(obj)) { return arrayToPromise(ctx, obj); } // 根据对象属性把全部属性转为一个Promise if (isObject(obj)) { return objectToPromise(ctx, obj); } // 基础数据类 1 , true return obj; }
转换方法这个懒得说了
function thunkToPromise(ctx, fn) { return new Promise(function _p(resolve, reject) { fn.call(ctx, function _(err, res) { if (err) { return reject(err); } if (arguments.length > 2) { res = slice.call(arguments, 1); } resolve(res); }); }); } function arrayToPromise(ctx, obj: any[]) { return Promise.all(obj.map((item) => toPromise(ctx, item))); } function objectToPromise(ctx, obj) { const results = {}; const keys = Object.keys(obj); const promises = []; for (let i = 0, len = keys.length; i < len; i++) { const key = keys[i]; const val = obj[key]; const promise = toPromise(ctx, val); if (promise && isPromise(promise)) { promises.push(promise.then(function _(res) { results[key] = res; })); } else { results[key] = val; } } return Promise.all(promises).then(function _() { return results; }); }
还有一些判断工具函数
function isPromise(obj: { then: Function) { return "function" === typeof obj.then; } function isGenerator(obj) { return "function" === typeof obj.next && "function" === typeof obj.throw; } function isGeneratorFunction(obj) { const constructor = obj.constructor; if (!constructor) { return false; } if ("GeneratorFunction" === constructor.name || "GeneratorFunction" === constructor.displayName) { return true; } return isGenerator(constructor.prototype); } function isObject(val) { return Object === val.constructor; }
此次逼着本身写3天就写完了,果真就是懒。
接下来写一个系列文章preact
的源码解析与实现。
尽可能一周出一篇?看看状况吧。