会用不如会造 - Promise
Promise ?
为什么要自己造 Promise
Promise已经是Ecmascript 6标准中的内容
Promise是async & await的基础
不造一下,怎么才能把Promise/A+的标准了解的更透彻?
本文结构
Promise认知 - 40%
Promise轮子 - 60%
对Promise有足够了解的读者,可酌情阅读跳过认知部分。
Promise的认知阶梯
最早开始使用Promise是为了不写回调形式的函数;后来发现Promise可以让nodejs里function (err, data)
的写法变得更友好;后来开始用 q;再后来发现 bluebird 这样顺应 Promise/A+ 的API用起来更顺。然后发现bluebird这个词原本就是一个combinator,compose其实是它的本意;然后发现Promise也是一种Monad。
以上是笔者自己对Promise的认知之路,理解上不断产生新的变化,其中真正可以提炼出来的步骤如下:
Phase 1 - Callback Hell 救星
首先,callback是异步操作的产物,同步操作不需要回调函数。早先在纯浏览器环境中,会用到callback的有:
ajax请求
DOM事件绑定
setTimeout
从nodejs开始,异步IO是根本特性,因而callback变得无处不在,然后出现了nodejs的callback参数标准:function (err, data)
/* * callback version */var sillyCopy = function (src, target, callback) { fs.readFile(src, function (err, data) { if (err) return callback(err); fs.write(target, data, function (err) { if (err) return callback(err); fs.write(src, 'this is silly', function (err) { callback(err); }); }); }); sillyCopy('my/dir/src.txt', 'my/dir/target.txt', function (err) { if (err) console.log(err); else console.log('done'); });/* * promise version */var sillyCopy = function (src, target) { return promiseFS.readFile(src) .then(function (data) { return promiseFS.writeFile(target)); }) .then(function() { return promiseFS.writeFile(src, 'this is silly'); }); }; sillyCopy('my/dir/src.txt', 'my/dir/target.txt') .then(function (data) { console.log(data); }, function (err) { console.log(err); });
注: 以上 promise 版本的 sillyCopy 中用到了promise化的 fs。
可以看到,使用了promise之后的异步操作定义,变得更加更加清晰
Phase 2 - 串联异步操作
var flow = function (list) { return function (arg) { return list.reduce(function (p, fn) { return p.then(fn); }, Promise.resolve(arg)); };var sillyProcess = flow([ pseudo_ReadFile, pseudo_AjaxPostSearchFileText, pseudo_ReadDB, pseudo_WriteToLocalFile ])
这里的flow就是同步模式下用到的 compose,用于将各种异步操作进行串联,在充满异步操作充满Promise的环境中,一个简单的flow实现可以让代码变得清晰易懂
Phase 3 - 异常捕获
回看之前的样例代码,callback形式会让对异常的捕获散布在各层级的callback里,看着闹心有没有?
var sillyCopy = function (src, target, callback) { fs.readFile(src, function (err, data) { if (err) return callback(err); fs.write(target, data, function (err) { if (err) return callback(err); fs.write(src, 'this is silly', function (err) { callback(err); }); }); });// This is what we wantsillyCopy(src, target) .catch(function (err) { console.log(err); });
在实际Coding过程中,异常处理是非常重要的步骤,除了上面看到的串行的异步代码,还有并行的异步操作,各种复杂情况导致对异常的统一处理变得十分关键,仅异常处理的代码会变得更容易维护。
Phase 4 - Thenable 接口
Thenable在一般场景中我们碰到的不算特别多,但它的价值却不能被低估。Thenable的价值在于,它是一个统一的接口定义,它让我们可以把不同Library里的、使用不同Promise实现的异步操作,放在一起使用。
例如,jQuery有它自己内部对Promise的实现 (早起使用 defer 创建 Promise 对象 ,后来慢慢统一到了 Promise/A+ 的标准模式)。还有 npm 上成千上万的包,都可能使用了不同的 Promise 实现。
开始造 Promise
既然是造轮子,先看下按照什么标准来造,然后确定一下造的步骤,剩下的就是开始coding了。
标准
步骤
我们把实现过程分成3部分,类似于对Promise的认知过程
串联异步操作,忽略异常处理
构造函数
保留 resolve 结果
then,要满足 resolve 之后的 then 也能得到执行
异常处理
then增加reject参数
捕获 resolve 和 reject 过程中的异常
Thenable
允许返回一个 Thenable 的对象
对 Thenable.then(resolve, reject) 的整体过程进行异常捕获
串联异步操作
一个Promise对象可以多次调用 then
保存 resolve 结果
var MyPromise = function (executor) { var self = this; this.next = []; this._value = null; this._state = 0; var resolve = function (value) { self._state = 1; self._value = value; self.next.forEach(function (fn) { fn(value); }); }; executor(resolve); }; MyPromise.prototype.then = function (onResolve) { var self = this; if (!self._state) { return MyPromise.resolve(onResolve(self._value)); } return new Promise(function (resolve) { self.next.push(function (value) { resolve(onResolve(value)); }); }); }; MyPromise.resolve = function (value) { return new Promise(function (resolve) { resolve(value); }); };/* * Demo */var p = Promise.resolve(2); p .then(function (a) { return a * 2 }) .then(function (a) { console.log(a) });// output: 4setTimeout(function () { p .then(function (a) { return a * 3 }) .then(function (a) { console.log(a) }); // output: 6}, 100);
至此,串联异步操作部分就算完成了,一个不带 reject 和 异常捕获的 Thenable 实例。从上面的 Demo 可以看出,我们的 Promise API 已初具雏形。但不要得意的太早,Promise 的 API 本身并不复杂:
Promise API
Promise Constructor
Promise.prototype.then
Promise.prototype.catch
Promise.all
Promise.resolve
Promise.reject
不过同等功能下,越是简单的 API 设计,其内部逻辑就会越复杂。jQuery.$ 就是一个很好的例子。
异常捕获
then 接受两个参数, onResolve & onReject
对 onResolve 和 onReject 执行过程中产生的异常进行捕获
var PENDING = 0;var RESOLVED = 1;var REJECTED = 2;// Note: onResolve 和 onReject 都会在这里包一层// 为了让两者的返回值都能继续向下传递到下一个 onResolvevar wrapHandler = function (state, handler, p2) { return function (val) { var next = state == RESOLVED ? p2._resolve : p2._reject; var ret, then, type, p3; if (!handler || typeof handler !== 'function') { return next(val); } try { ret = handler(val); } catch (e) { return p2._reject(e); } p2._resolve(ret); }; };var genHandler = function (state, p) { return function (value) { if (p._state !== PENDING) return; p._state = state; p._value = value; p.next.forEach(function (obj) { setTimeout(function () { obj[state === RESOLVED ? 'onResolve' : 'onReject'](value); }, 0); }); }; };var MyPromise = function (executor) { var self = this; this.next = []; this._value = null; this._state = PENDING; this._resolve = genHandler(RESOLVED, self); this._reject = genHandler(REJECTED, self); executor(this._resolve, this._reject); }; MyPromise.prototype.then = function (onResolve, onReject) { var self = this; var p2 = new MyPromise(function () {}); var ret; if (self._state === PENDING) { self.next.push({ onResolve: wrapHandler(RESOLVED, onResolve, p2), onReject: wrapHandler(REJECTED, onReject, p2) }); } else { setTimeout(function () { wrapHandler(self._state, self._state === RESOLVED ? onResolve : onReject, p2)(self._value); }, 0); } return p2; }; MyPromise.resolve = function (value) { return new Promise(function (resolve, reject) { resolve(value); }); }; MyPromise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); };/* * Demo */MyPromise.reject(1) .then(null, function (e) { console.log(e); return e + 2; }) .then(function (n) { console.log(n); throw n * 2; }) .then(null, function (e) { console.log(e); });// output: 1// output: 3// output: 6
这里有几个非常重要的 Promise 使用方法:
没有设置 onResolve 或 onReject,对应的返回值或异常将原封不动向下传递
Promise.resolve(1) .then() .then(function (n) { console.log(n); });// output: 1Promise.reject(2) .then() .then(null, function (e) { console.log(e); });// output: 2
onReject 的返回值会继续向下传递给下一个 onResolve
Promise.reject(1) .then(null, function (e) { return e + 2; }) .then(function (n) { console.log(n); };// output: 3
Thenable 接口
Thenable 类型的处理
出现返回Promise实例自身的情况,要抛出 TypeError
var PENDING = 0;var RESOLVED = 1;var REJECTED = 2;var wrapHandler = function (state, handler, p2) { return function (val) { var next = state == RESOLVED ? p2._resolve : p2._reject; var ret, then, type, p3; if (!handler || typeof handler !== 'function') { return next(val); } try { ret = handler(val); } catch (e) { return p2._reject(e); } solve(ret, p2); }; };// 处理所有可能情况的 val,包括 Thenablevar solve = function (val, p2) { if (val === p2) { return p2._reject(new TypeError('no promise circle allowed')); } type = typeof val; if (type === 'function' || type === 'object' && val !== null) { try { then = val && val.then; } catch (e) { return p2._reject(e); } if (typeof then === 'function') { try { return then.call(val, function (val2) { solve(val2, p2); }, p2._reject); } catch (e) { return p2._reject(e); } } } return p2._resolve(val); }var genHandler = function (state, p) { return function (value) { if (p._state !== PENDING) return; p._state = state; p._value = value; p.next.forEach(function (obj) { setTimeout(function () { obj[state === RESOLVED ? 'onResolve' : 'onReject'](value); }, 0); }); }; };var MyPromise = function (executor) { var self = this; this.next = []; this._value = null; this._state = PENDING; this._resolve = genHandler(RESOLVED, self); this._reject = genHandler(REJECTED, self); executor(this._resolve, this._reject); }; MyPromise.prototype.then = function (onResolve, onReject) { var self = this; var p2 = new MyPromise(function () {}); var ret; if (self._state === PENDING) { self.next.push({ onResolve: wrapHandler(RESOLVED, onResolve, p2), onReject: wrapHandler(REJECTED, onReject, p2) }); } else { setTimeout(function () { wrapHandler(self._state, self._state === RESOLVED ? onResolve : onReject, p2)(self._value); }, 0); } return p2; }; MyPromise.resolve = function (value) { return new Promise(function (resolve, reject) { resolve(value); }); }; MyPromise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); };
到此为止,一个满足 Promise/A+ 规范的 MyPromise 就算造完了。
作者:kdepp
链接:https://www.jianshu.com/p/90ef2ae6572d
共同学习,写下你的评论
评论加载中...
作者其他优质文章