Javascript Promise 学习(上)
Promise 就是处理异步的一个规范方法
a();
b();
alert("a");
如果a() 里面有一个ajax 或者settimeout
那么alert("a") 会先跑
这就是异步了。
从前我们用一堆callBack函数来解决问题,但是这样写不好看。
promise 的写法美丽多了
依据上面的例子
a().then(b).then(function(){
alert("");
})
这样它会先跑完 a -> b - > alert("");
虽然很多类库都实现了这个功能,甚至游览器也有自带的,而且ecma6好像还会有更好更简单的方法来解决异步的操作 !
but ! 人生最鸡巴的就是这个 but!
当作学习,我还是自己写了一个版本。
这个版本不是 promise a 也不是promise a+ 也不是jQuery Defferd 规范
它是给我自己用的!
个人觉得重点思想就是在 then 的时候返回一个 next promise , 而next promise 记入在上一个promise中,这样就创建了一个promise 连环链
而一个回调函数返回的是promise 或者我们叫thenAble函数,那么我们就把原先的链连接去它的后面就可以了。
function Promise(fn) { //如果有放fn进来,那么就调用它,把 pass & fail 做参数传进去 //这样fn 内就可以直接 publish了 if (G.isFunction(fn)) { var promise = new Promise(); fn.apply(null, [promise.pass.bind(promise), promise.fail.bind(promise)]); return promise; } this.status = "pending"; this.data; this._nextPromise; //保存下一个的指针,这样才可以连环触发 this._passFn_list = []; this._failFn_list = []; this._isAllPassRequest = true; //all or any 用的 //注释 //要改要潜水 //这里只说简单的使用说明吧 /* //方便的初始化调用法 new Promise(function (pass, fail) { setTimeout(pass, 100, "zzz"); //第三para是传给方法的参数 = pass(zzz) }).then(fnPass, fnFail); //一般调用法 a().then(b).then(c); function a() { var promise = new Promise(); setTimeout(function () { promise.pass("data"); }, 1000); return promise; } //all 和 any 参数是array,它会并发处理 //等待全部处理完验证每个的返回 //unknow 算 true //(note: 一个promise只保存all or any 1condition default是 all) //如果要求是 all ,那么回来的全部都必须all 就调用nextPromise.pass else fail //如果要求是 any ,那么回来的要有至少一个是pass 就调用nextPromise.pass else fail a().all([b, c], [c, b]).then(b, c); //.catch, .complete 都是语法糖罢了 //异步loop调用, 这个比较复杂,以后还可以有更多变化 var list = [a, a, a]; var promiseTemp; for (var i = 0; i < list.length; i++) { var promise = (promiseTemp == undefined) ? list[i]() : promiseTemp.then(list[i]); promiseTemp = promise; } */ //简单说明: /* 两大关键方法 then : 把参数加入fn_list中, 然后new 新的promise, 当前的promise.next 指向新的promise(循环链就开始了) ,然后把新的promise 返回出去 pass/fail : 把fn_list中的方法全部拿出来,然后"并发处理", 检查每一个是不是返回promise, 是的话要记入和跟踪,跟踪其实就是为这个promise加一个then,然后把职责链放进去,交替的感觉。 调用then/all/any的时候如果promise的status不是pending会直接add and 发布data 在并发方法之后 fnList会被清除干净,也就是说一个promise的fnlist的每个方法只会被执行一次 */ } Promise.prototype._concatFnList = function (passFn_list, failFn_list) { //all 和 any 用来concat fn list 的 if (passFn_list != undefined && Array.isArray(passFn_list)) { this._passFn_list = this._passFn_list.concat(passFn_list); } if (failFn_list != undefined && Array.isArray(failFn_list)) { this._failFn_list = this._failFn_list.concat(failFn_list); } } Promise.prototype._getFinalDataByDataRecordList = function (dataRecord_list) { //dataRecord_list 是array , 因为大部分时候我们是用一个data罢了,所以这边做一些小过滤 var finalValue; if (dataRecord_list.length == 1) { finalValue = dataRecord_list[0].data; //抽取data出来就够了 } else { dataRecord_list.orderBy("index");//排好位置(因为all/any是并发,不会按照顺序回来) finalValue = dataRecord_list.map(function (obj) { delete obj.index; //洗掉属性 index 排位之后就没用了 return obj; }); } return finalValue; } Promise.prototype._publish = function (fn_list, that) { //pass 和 fail 的共用过程 if (fn_list.length > 0) { var returnPromiseCountRecord = 0; //for all & any 多个并发,同时会有多个promise函数 var dataRecord_list = []; //收集全部函数回来的data {index,data,status} for (var i = 0, l = fn_list.length; i < l; i++) { var fn = fn_list[i]; var returnValue = fn.call(that, that.data); //挺关键的一句,这里会确定他返回的是不是promise (function (i) { function callBack(data) { //这里是我们外面的promise链 publish触发的 var result = { index: i, status: this.status, data: data }; dataRecord_list.push(result) //record data returnPromiseCountRecord--; //之前我们累加计算,这里publish了就要开始累减回去了 //一直到0就准备做ending然后继续外面的职责链了 if (returnPromiseCountRecord == 0) { var is_allPass = dataRecord_list.every(function (v) { return (v.status == "unknow" || v.status == "pass"); }); var is_anyPass = dataRecord_list.some(function (v) { return (v.status == "unknow" || v.status == "pass"); }); var finalValue = that._getFinalDataByDataRecordList(dataRecord_list); //这里会依据 all or any 做处理 觉得调用下一家的 pass or fail if ((that._isAllPassRequest && is_allPass) || (!that._isAllPassRequest && is_anyPass)) { that._nextPromise.pass(finalValue); } else { that._nextPromise.fail(finalValue); } } } if (returnValue instanceof Promise) { //是promise的话就要 +count,然后+then给他,把职责链给他,等待他触发 returnPromiseCountRecord++; var otherPromise = returnValue; otherPromise.complete(callBack); } else { //不是的话就push data 就好,status : unknow 也算 pass var result = { index: i, status: "unknow", data: returnValue }; dataRecord_list.push(result); } })(i) } fn_list.length = 0; //运行完就clear掉 //没有return任何thenable方法 if (returnPromiseCountRecord == 0) { var finalValue = that._getFinalDataByDataRecordList(dataRecord_list); that._nextPromise.pass(finalValue); //因为没有pass fail 参考所以一定是pass } } else { //如果没有效应就去找小一家promise效应 if (that._nextPromise) { that._nextPromise[that.status](that.data); } } } Promise.prototype.all = function (passFn_list, failFn_list) { this._concatFnList(passFn_list, failFn_list); if (this.status != "pending") this._alreadyPublish(); return this._nextPromise || (this._nextPromise = new Promise()); } Promise.prototype.any = function (passFn_list, failFn_list) { this._concatFnList(passFn_list, failFn_list); this._isAllPassRequest = false; if (this.status != "pending") this._alreadyPublish(); return this._nextPromise || (this._nextPromise = new Promise()); } Promise.prototype.then = function (passFn, failFn) { if (passFn != undefined) this._passFn_list.push(passFn); if (failFn != undefined) this._failFn_list.push(failFn); if (this.status != "pending") this._alreadyPublish(); return this._nextPromise || (this._nextPromise = new Promise()); } Promise.prototype._alreadyPublish = function () { var that = this; setTimeout(function () { that[that.status](that.data); }, 0); } Promise.prototype.pass = function (data) { var that = this; this.status = "pass"; this.data = data; var passFn_list = this._passFn_list; that._publish(passFn_list, that); } Promise.prototype.fail = function (data) { var that = this; this.status = "fail"; this.data = data; var failFn_list = this._failFn_list; that._publish(failFn_list, that); } Promise.prototype.complete = function (completeFn) { return this.then(completeFn, completeFn); } Promise.prototype.catch = function (catchFn) { return this.then(void 0, catchFn); } Promise.prototype.getLastPromise = function () { var promise = this; for (var i = 0; i < Number.MAX_VALUE; i++) { if (promise._nextPromise === undefined) { return promise; } else { promise = promise._nextPromise; } } } G.s.Promise = Promise;