链式调用
上文有提到标准 Promise 的一些特性,接下来一起来实现 Promise 的链式调用。
来看看标准 Promise 的链式调用:var fn = new Promise(function (resolve, reject) { setTimeout(function() { resolve('oook~') }, 500) }) fn.then(function(data) { console.log('data-1: ', data) return new Promise(function(resolve, reject) { setTimeout(function() { reject('不oook~') }, 1000) }) }, function(err) { console.log('err-1: ', err) }) .then(function(data) { console.log('data-2:', data) }, function(err) { console.log('err-2: ', err) }) .then(function(data) { console.log('data-3:', data) }, function(err) { console.log('err-3: ', err) })复制代码可以看到,经过0.5秒后输出 data-1: oook~,又经过1秒后输出 err-2: 不oook~,最后输出 data-3: undefined
特征如下:
- then 方法可以返回普通值,也可以返回一个 Promise 对象。
- 如果 then 方法返回的是一个 Promise 对象,则会根据这个 Promise 对象里执行的是 resolve 还是 reject 走后一个 then 方法的成功和失败回调(本例在第一个 then 里返回了一个 Promise 对象,并异步调用了 reject ,所以会输出 err-2: 不oook~)。
- 如果 then 方法返回的是一个普通值(无论走的成功还是失败),那么这个普通值就会被当做下一个 then 里成功回调函数的参数(上面例子最后打出 err-3: undefined 就是因为函数默认返回了普通值 undefined)。
根据以上特征,我们的 Promise 对象需要增加以下处理:
- 只有 Promise 对象才有 then 方法,所以我们的 then 方法要支持链式调用的话,需要返回一个新的 Promise 对象,并在这个对象里对要传递的值进行处理
- 由于 then 方法的返回值可能是一个普通值,也可能是一个新的 Promise 对象,Promise 对象里可能又是一个 Promise 对象,所以我们需要对这个返回值做一个统一的处理,本例用一个统一的方法 resolvePromise 来处理
代码如下:
Promise.prototype.then = function (onFulfilled, onRejected) { var _this = this // 定义一个promise2变量(别问我为什么这么命名,A+规范里就是这么命名的) var promise2 if (_this.status === 'fulfilled') { // 返回一个新的promise promise2 = new Promise(function (resolve, reject) { // A+ 规范规定,onFulfilled 和 onRejected 都需要异步执行,所以加上一个定时器 setTimeout(function() { try { // x可能是个普通值,也可能是个promise var x = onFulfilled(_this.successVal) // x也可能是第三方的 Promise 库,用一个函数统一来处理 resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }); } if (_this.status === 'rejected') { promise2 = new Promise(function (resolve, reject) { setTimeout(function() { try { var x = onRejected(_this.failVal) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) } // 当调用then时,如果是异步操作,就会处于pending状态 if (_this.status === 'pending') { // 将成功的回调添加到数组中 promise2 = new Promise(function (resolve, reject) { _this.onFulfilledList.push(function () { // 可以看到,我们在每一个 resolve 和 reject 执行的地方都加上了异步 setTimeout(function() { try { var x = onFulfilled(_this.successVal) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) _this.onRejectedList.push(function () { setTimeout(function() { try { var x = onRejected(_this.failVal) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) }) } return promise2 } function resolvePromise(p2, x, resolve, reject) { // 不能返回自己 if (p2 === x) { return reject(new TypeError('不能循环引用!')) } // 上一篇有说过,resolve 和 reject 不能同时执行,所以用一个锁来当开关 var called // x返回的可能是对象和函数也可能是一个普通的值 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { var then = x.then if (typeof then === 'function') { then.call(x, function (y) { // ------- resolve 和 reject 不能同时执行 ------- if (called) { return } called = true // 如果成功的话,y有可能也是个 promise,所以递归继续解析 resolvePromise(p2, y, resolve, reject) }, function (e) { // ------- resolve 和 reject 不能同时执行 ------- if (called) { return } called = true reject(e) }) } else { // 如果是普通值,直接返回成功 resolve(x) } } catch(e) { if (called) { return } called = true reject(e) } } else { resolve(x) } }复制代码
ok,链式调用的处理基本上就完成了,下面来看看效果:
var p = new Promise(function(resolve, reject){ setTimeout(function() { resolve('完全ooooook~') }, 500) }) p.then(function (data) { console.log('success1: ', data) return new Promise(function(resolve, reject) { setTimeout(function() { reject('啊啊啊啊啊啊啊啊啊') }, 1000) }) }, function (err) { console.log('failed: ', err) }) .then(function (data) { console.log('success2: ', data) }, function(err) { console.log('failed2: ', err) })复制代码
结果如下:
结果完全ooooook啊啊啊啊啊啊