promise源码解析
主要内容:promise的迭代设计中主要的代码片段,翻译一部分加上自己的理解,同时指出promise的一些特性。
promise的雏形
var maybeOneOneSecondLater = function () { var callback; setTimeout(function () { callback(1); }, 1000); return { then: function (_callback) { callback = _callback; } }; }; maybeOneOneSecondLater().then(callback1);
promise诞生,比起传统回调,它返回了一个表示最终结果的对象(一个承诺),我们可以在这个对象上调用函数观察它的实现或者是拒绝。
但本质上还是传入回调,任人宰割。
好的是,promise在异步操作和回调之间提供了一个中间层,在这个中间层里可以搞一些事情,解决传统回调的蛋疼问题。
改进后的promise可以让我们的回调不’任人宰割‘了。
第一次改进
很明显目前这个promise只能处理一个回调,传入多个回调会覆盖,所以数据结构上应该用一个数组来储存回调函数,需要执行的时候,依次执行。
let defer = () => { let pending = [],value; return { resolve(_value){ value = _value for(let i = 0;i < pending.length; i++){ pending[i](value) } pending = undefined; }, then(_callback){ if(pending){ pending.push(_callback) }else{ _callback(); } } } } let oneOneSecondLater = () => { let result = defer(); setTimeout(()=> { result.resolve(1); }, 1000); return result; }; oneOneSecondLater().then(callback);
这里对代码做一些解释:
- defer函数的作用:产生promise对象和resolve函数,可以理解为构造函数;
- resolve函数是你在封装异步函数时用的,promise是你使用异步函数时用的,defer函数像是一个中介,给两头服务;
- oneOneSecondLater函数封装了我们的异步操作setTimeout;
- result.resolve():异步操作完成后告诉promise,promise会替你执行回调,这是它作为一个中介应该做的;
- oneOneSecondLater().then(callback):oneOneSecondLater函数的使用者需要告诉promise,成功后执行什么;
好了,目前我们的promise可以接受多次回调,并在异步操作完成后顺序执行了。
第二次改进
promise这个中介规定,异步操作只能成功一次(resove只能调用一次哟)。
也就是说,使用promise封装异步操作的同事们不可能让你的回调执行两次了。。你就大胆的传进去吧。。
let defer = () => { let pending = [],value return { resolve(_value){ if(pending){ value = _value for(let i = 0;i < pending.length; i++){ pending[i](value) } pending = undefined; }else{ throw new Error("A promise can only be resolved once.") } }, then(_callback){ if(pending){ pending.push(_callback) }else{ _callback(); } } } }
补充一点:
- 因为promise在异步操作成功后,就将pending设为了undefined,这也说明,promise向我们保证了:异步状态一旦改变,就定格了。
- 所以如果一个异步操作已经成功,你再传回调进去,那就会直接执行:if(pending){pending.push(_callback)}else{_callback();}
第三次改进:职责分离
let defer = () => { let pending = [],value; return { resolve(_value){ if(pending){ value = _value for(let i = 0;i < pending.length; i++){ pending[i](value) } pending = undefined; }else{ throw new Error("A promise can only be resolved once.") } }, promise: { then (callback) { if (pending) { pending.push(callback); } else { callback(value); } } } } }
- 这个改进就很小了,只是把then封装到promise对象中,让resolve和promise两个对象各司其职;
- resolve是在封装异步操作的时候用的,promise是在使用异步操作时候用的;
第四次改进:加链式操作
熟悉promise的同学应该知道,每次then执行完成后都是会默认返回promis的,就是为了方便链式操作。
先贴上完整代码:
var ref = function (value) { if (value && typeof value.then === "function") return value; return { then: function (callback) { return ref(callback(value)); } }; }; var defer = function () { var pending = [], value; return { resolve: function (_value) { if (pending) { value = ref(_value); // values wrapped in a promise for (var i = 0, ii = pending.length; i < ii; i++) { var callback = pending[i]; value.then(callback); // then called instead } pending = undefined; } }, promise: { then: function (_callback) { var result = defer(); // callback is wrapped so that its return // value is captured and used to resolve the promise // that "then" returns var callback = function (value) { result.resolve(_callback(value)); }; if (pending) { pending.push(callback); } else { value.then(callback); } return result.promise; } } }; };
直接看是有点懵逼的。。
分开来看一下,先看这一段promise对象:
promise: { then: function (_callback) { var result = defer(); // callback is wrapped so that its return // value is captured and used to resolve the promise // that "then" returns var callback = function (value) { result.resolve(_callback(value)); }; if (pending) { pending.push(callback); } else { value.then(callback); } return result.promise; } }
- 链式操作的形式:xxx.then(callback1).then(callback2)
- 也就是说我们的then函数返回的是promise对象;
- 所以在then函数的开始, var result = defer();创建一个空promise对象。最后return result;
- callback1在执行后要把结果给callback2吧,怎么给呢?
- 先执行回调:callback1(value)
- 执行完了通知下一个promise可以执行了:result.resolve()
- 合体:result.resolve(callback1(value))
再看这一段resolve函数:
resolve: function (_value) { if (pending) { value = ref(_value); // values wrapped in a promise for (var i = 0, ii = pending.length; i < ii; i++) { var callback = pending[i]; value.then(callback); // then called instead } pending = undefined; } },
- resolve(_value)中的_value是封装者给回调的参数;
- _value可能是一个promise吗?当然可以;
- ref函数就是为了判断_vlaue是不是promise,如果是则原样返回,不是的话包装成一个promise返回,方便我们统一处理;
- 经过ref处理过的value肯定是一个promise了,所以我们统一写成:value.then(callback)
贴一下ref:
var ref = function (value) { if (value && typeof value.then === "function") return value; return { then: function (callback) { return ref(callback(value)); } }; };
- 这里看到,如果vlaue是promise,就直接返回;
- value如果不是promise,包装成promise,把value传入callback;
所以ref只是作者抽出去的一个工具函数哈,其实不抽的话更容易看懂= =!
把ref合进resolve,大家看看是不是容易理解了:
resolve: function (_value) { if (pending) { if(_value && typeof _value.then === "function") { for (var i = 0, ii = pending.length; i < ii; i++) { var callback = pending[i]; _value.then(callback); } }else { for (var i = 0, ii = pending.length; i < ii; i++) { pending[i](_value); } } pending = undefined; } },
这种写法没有保存_value到value中,仅仅是为了解释resolve的一段代码
增加错误处理
目前来看的话,promise只接受了一个回调,很明显这里需要再接受一个错误回调,根据异步操作的执行结果,选择执行哪个。
先贴完整代码:
var defer = function () { var pending = [], value; return { resolve: function (_value) { if (pending) { value = ref(_value); for (var i = 0, ii = pending.length; i < ii; i++) { value.then.apply(value, pending[i]); } pending = undefined; } }, promise: { then: function (_callback, _errback) { var result = defer(); // provide default callbacks and errbacks _callback = _callback || function (value) { // by default, forward fulfillment return value; }; _errback = _errback || function (reason) { // by default, forward rejection return reject(reason); }; var callback = function (value) { result.resolve(_callback(value)); }; var errback = function (reason) { result.resolve(_errback(reason)); }; if (pending) { pending.push([callback, errback]); } else { value.then(callback, errback); } return result.promise; } } }; }; let ref = (value) => { if (value && typeof value.then === "function") return value; return { then: function (callback) { return ref(callback(value)); } }; }; let reject = (reason) => { return { then: function (callback, errback) { return ref(errback(reason)); } }; };
安全性和稳定性
保证callbacks和errbacks在未来他们被调用的时候,应该是和注册时的顺序是保持一致的。这将显著降低异步编程中流程控制出错可能性。
let enqueue = (callback) => { setTimeout(callback,1) } resolve: function (_value) { if (pending) { value = ref(_value); for (let i = 0, ii = pending.length; i < ii; i++) { enqueue(function () { value.then.apply(value, pending[i]); }); } pending = undefined; } } let ref = function (value) { if (value && value.then) return value; return { then: function (callback) { let result = defer(); // XXX enqueue(function () { result.resolve(callback(value)); }); return result.promise; } }; }; let reject = function (reason) { return { then: function (callback, errback) { var result = defer(); // XXX enqueue(function () { result.resolve(errback(reason)); }); return result.promise; } }; };