完全copy from vue-resource (The HTTP client for Vue.js)
https://github.com/pagekit/vue-resource
理解这个实现,则对promise的语法就不难理解了,不然一直云里雾里的死记硬背语法。
1.1 promise构造函数
/**
* Promises/A+ polyfill v1.1.4 (https://github.com/bramstein/promis)
*/
// 根据promise对象的三种状态定义三个常量
var RESOLVED = 0;
var REJECTED = 1;
var PENDING = 2;
// 构造函数
function Promise$1(executor) {
this.state = PENDING; // 初始化实例状态为pending
this.value = undefined; // 操作结果,初始化为undefined
this.deferred = []; // 延迟操作数组,既then()添加的成功or失败函数
var promise = this; // 保存this指向,在引入的操作函数中用
try {
executor(function (x) { // 执行操作,并传入实例的resolve/reject方法
promise.resolve(x); // 当传入的操作明确执行成功,调用实例resolve方法
}, function (r) {
promise.reject(r); // 当传入的操作明确执行失败,调用实例reject方法
});
} catch (e) {
promise.reject(e); // 当操作执行过程中,语法或其他意外错误,同样调用实例reject方法
}
}
1.2 原型上挂载的方法
var p$1 = Promise$1.prototype; // 原型
p$1.resolve = function resolve(x) {
var promise = this;
if (promise.state === PENDING) {
if (x === promise) {
throw new TypeError('Promise settled with itself.');
}
var called = false;
try {
var then = x && x['then'];
// 如果操作结果返回的x是另一个promise实例,则当前promise实例的状态则重新由
// 返回的promise实例的状态来决定
if (x !== null && typeof x === 'object' && typeof then === 'function') {
then.call(x, function (x) { // 调用返回的实例的then方法,获取返回的实例的状态
if (!called) {
promise.resolve(x); // 成功,则执行当前实例的resolve,并返回
} // 返回的实例的执行结果
called = true;
}, function (r) {
if (!called) {
promise.reject(r); // 失败
}
called = true;
});
return;
}
} catch (e) {
if (!called) {
promise.reject(e);
}
return;
}
//当执行结果是普通(非promise实例)结果,才会走到这一步,改变状态且执行延迟操作
promise.state = RESOLVED; // 实例状态从pendding-->fulfiled
promise.value = x; // 把操作结果挂载到实例上
promise.notify(); // 触发实例上挂载的延迟操作
}
};
p$1.reject = function reject(reason) {
var promise = this;
if (promise.state === PENDING) {
if (reason === promise) {
throw new TypeError('Promise settled with itself.');
}
// 同上
promise.state = REJECTED;
promise.value = reason;
promise.notify();
}
};
// 触发通过then方法挂载到实例上的延迟操作
// 两种情况会触发,1 操作有结果时,调用resolve/reject实例方法会触发
// 2 调用then实例方法,挂载延迟操作时,会触发,这样可以保证操作和回调解耦的情况下,回调能执行。
p$1.notify = function notify() {
var promise = this;
// nextTick既微任务队列,vue-resouure里面的nextTick直接使用的Vue里面提供的Vue.nextTick,
// Vue里面则根据浏览器宿主环境选择原生promise或者Observe以及setTImeout
nextTick(function () {
// 只有状态变化才会执行队列
if (promise.state !== PENDING) {
while (promise.deferred.length) { // 遍历延迟操作数组
var deferred = promise.deferred.shift(),
onResolved = deferred[0],
onRejected = deferred[1],
resolve = deferred[2],
reject = deferred[3];
try {
if (promise.state === RESOLVED) { // 成功延迟操作
if (typeof onResolved === 'function') {
// 注意:这里的resolve是then方法调用生成的新的promise实例,
//所以这里返回的是新实例的结果
resolve(onResolved.call(undefined, promise.value));
} else {
// 如果函数未传入,则把结果向后推送
resolve(promise.value);
}
} else if (promise.state === REJECTED) { // 失败延迟操作
if (typeof onRejected === 'function') {
// 同理,reject也是新实例的方法,如果处理了失败,
// 失败状态不会向下传递,且新的实例状态为fullfied
resolve(onRejected.call(undefined, promise.value));
} else {
//否则,失败结果会向下传递,这也是为什么失败延迟操作,可以捕获链式
// 调用中,某一环节未被处理的错误
reject(promise.value);
}
}
} catch (e) {
reject(e);
}
}
}
});
};
// 挂载延迟操作回调
p$1.then = function then(onResolved, onRejected) {
var promise = this;
// 返回一个新的实例
return new Promise$1(function (resolve, reject) {
// 向旧的实例的延迟操作队列中push新的
promise.deferred.push([onResolved, onRejected, resolve, reject]);
// 触发执行队列
promise.notify();
});
};
p$1.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
1.3构造函数挂载的方法
//构造函数挂载的reject,用于快速生成一个明确pendding->failed的实例
Promise$1.reject = function (r) {
return new Promise$1(function (resolve, reject) {
reject(r);
});
};
//构造函数挂载的reject,用于快速生成一个明确pendding->fullfied的实例
Promise$1.resolve = function (x) {
return new Promise$1(function (resolve, reject) {
resolve(x);
});
};
Promise$1.all = function all(iterable) {
return new Promise$1(function (resolve, reject) {
var count = 0,
result = [];
if (iterable.length === 0) {
resolve(result);
}
function resolver(i) {
return function (x) {
result[i] = x;
count += 1;
if (count === iterable.length) {
resolve(result);
}
};
}
for (var i = 0; i < iterable.length; i += 1) {
Promise$1.resolve(iterable[i]).then(resolver(i), reject);
}
});
};
Promise$1.race = function race(iterable) {
return new Promise$1(function (resolve, reject) {
for (var i = 0; i < iterable.length; i += 1) {
Promise$1.resolve(iterable[i]).then(resolve, reject);
}
});
};