自己动手实现一个Promise

36次阅读

共计 6923 个字符,预计需要花费 18 分钟才能阅读完成。

Promise 基本用法
Promise 对象是一个代理对象,被代理的值在 Promise 对象创建时可能是未知的。
它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的 promise 对象
一个 Promise 有以下几种状态:
pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。

pending 状态的 Promise 对象可能触发 fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers)就会被调用(then 方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当 Promise 状态为 fulfilled 时,调用 then 的 onfulfilled 方法,当 Promise 状态为 rejected 时,调用 then 的 onrejected 方法,所以在异步操作的完成和绑定处理方法之间不存在竞争)。
因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回 promise 对象,所以它们可以被链式调用。
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(‘foo’);
}, 300);
});

promise1.then(function(value) {
console.log(value);
// expected output: “foo”
});

console.log(promise1);
// expected output: [object Promise]
Promise/A+
然后我们来了解一下 Promise 规范 Promises/A+ 规范(英文版)Promises/A+ 规范(中文版)
Promise 的实现
Promise 是通过 new 创建的,可以通过构造函数模式或者是 ES6 的 class 来实现,这儿选择构造函数的方式来实现 Promise,首先先完成一个简易版本的 Promise。
function Promise(exector) {
var _this = this
this.status = ‘pending’
this.value = undefined

try {
exector(resolve, reject)
}catch(e) {
reject(e)
}

function resolve(value) {
if(_this.status === ‘pending’) {
_this.status = ‘fulfilled’
_this.value = value
}
}

function reject(value) {
if(_this.status === ‘pending’) {
_this.status = ‘rejected’
_this.value = value
}
}
}

Promise.prototype.then = function(resolveCallback, rejectCallback) {
if(this.status === ‘fulfilled’) {
resolve(this.value)
}

if(this.status === ‘rejected’) {
reject(this.value)
}
}

new Promise((resolve, reject)=> {
resolve(‘1’)
}).then((data)=> {
console.log(‘resolve’ + data)
}, (data)=> {
console.log(‘reject’ + data)
}) //resolve1

new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve(‘1’)
}, 1000)
}).then((data)=> {
console.log(‘resolve’ + data)
}, (data)=> {
console.log(‘reject’ + data)
}) // 无法正常输出
上面这个 promise 中 resolve 和 reject 在同步的情况下都能正常输出,但是现在却不支持异步,因为在异步的时候调用 then 的时候状态还是 ’pending’,所以 resolve/reject 并不能如期执行。
这个时候就需要一个存放后续调用事件的数组,当异步函数执行完毕后再执行数组中的函数。
改进后异步方法可以正常运行:
function Promise(exector) {
var _this = this
this.status = ‘pending’
this.value = undefined
this.resolveList = []
this.rejectList = []

try {
exector(resolve, reject)
}catch(e) {
reject(e)
}

function resolve(value) {
if(_this.status === ‘pending’) {
_this.status = ‘fulfilled’
_this.value = value
_this.resolveList.forEach(item=> {
item(_this.value)
_this.resolveList.shift()
})
}
}

function reject(value) {
if(_this.status === ‘pending’) {
_this.status = ‘rejected’
_this.value = value
_this.rejectList.forEach(item=> {
item(_this.value)
_this.rejectList.shift()
})
}
}
}

Promise.prototype.then = function(resolveCallback, rejectCallback) {
if(this.status === ‘fulfilled’) {
resolve(this.value)
}

if(this.status === ‘rejected’) {
reject(this.value)
}

if(this.status === ‘pending’) {
this.resolveList.push(resolveCallback)
this.rejectList.push(rejectCallback)
}
}

new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve(‘1’)
}, 1000)
}).then((data)=> {
console.log(‘resolve’ + data)
}, (data)=> {
console.log(‘reject’ + data)
})
链式调用
我们可以注意到 Promise 是可以链式调用的,这就需要 then 的方法返回一个 Promise 对象。
下面是一个链式调用的简单例子:
let promise = new Promise((resolve, reject)=> {
resolve(666)
})

promise.then(data=> {
console.log(data)
return data + 1
}).then(data=> {
console.log(data)
})
//666
//667
在 Promise 链中返回 Promise:
let promise1 = new Promise((resolve, reject)=> {
resolve(666)
})

let promise2 = new Promise((resolve, reject)=> {
resolve(999)
})

promise1.then(data=> {
console.log(data)

return promise2
}).then(data=> {
console.log(data)
})
//666
//999
关于这种写法需要注意的是,第二个完成处理程序被添加到第三个 promise 而不是 return 的 promise2,上面的例子等价于:
let promise1 = new Promise((resolve, reject)=> {
resolve(666)
})

let promise2 = new Promise((resolve, reject)=> {
resolve(999)
})

let promise3 = promise1.then(data=> {
console.log(data)

return promise2
})

promise3.then(data=> {
console.log(data)
})
//666
//999
当异步的时候调用 then 函数的时候状态为 pending, 这个时候同样需要返回一个 promise 方便后续的链式调用。
所以修改为链式调用后的代码为:
function Promise(exector) {
var _this = this
this.status = ‘pending’
this.value = undefined
this.resolveList = []
this.rejectList = []

try {
exector(resolve, reject)
}catch(e) {
reject(e)
}

function resolve(value) {
if(_this.status === ‘pending’) {
_this.status = ‘fulfilled’
_this.value = value
_this.resolveList.forEach(item=> {
item(_this.value)
_this.resolveList.shift()
})
}
}

function reject(value) {
if(_this.status === ‘pending’) {
_this.status = ‘rejected’
_this.value = value
_this.rejectList.forEach(item=> {
item(_this.value)
_this.rejectList.shift()
})
}
}
}

Promise.prototype.then = function(resolveCallback, rejectCallback) {
var _this = this
if(this.status === ‘fulfilled’) {
return new Promise((resolve, reject)=> {
var result = resolveCallback(_this.value)
if(result instanceof Promise) {
result.then(resolve, reject)
}else {
resolve(result)
}
})
}

if(this.status === ‘rejected’) {
return new Promise((resolve, reject)=> {
var result = rejectCallback(_this.value)
if(result instanceof Promise) {
result.then(resolve, reject)
}else {
reject(result)
}
})
}

if(this.status === ‘pending’) {
return new Promise((resolve, reject)=> {
_this.resolveList.push(function() {
var result = resolveCallback(_this.value)
if(result instanceof Promise) {
result.then(resolve, reject)
}else {
resolve(result)
}
})

_this.rejectList.push(function() {
var result = rejectCallback(_this.value)
if(result instanceof Promise) {
result.then(resolve, reject)
}else {
reject(result)
}

})

})
}
}

new Promise((resolve, reject)=> {
resolve(666)
}).then((data)=> {
console.log(‘resolve1:’ + data)
return 999
}).then((data)=> {
console.log(‘resolve2:’ + data)
})
//resolve1: 666
//resolve2: 999

new Promise((resolve, reject)=> {
resolve(666)
}).then((data)=> {
console.log(‘resolve1:’ + data)
return new Promise((resolve, reject)=> {
resolve(999)
})
}).then((data)=> {
console.log(‘resolve2:’ + data)
})
//resolve1: 666
//resolve2: 999
基本的功能已经实现,下面开始实现 Promise 的 all,race,resolve,reject 方法,链式调用部分思路借鉴 Promise/A+ 规范的实现
Promise.all()
该方法只接受一个有多个受监听的 Promise 的可迭代对象(比如数组),只有当可迭代对中所有 Promise 都被解决后才会返回 resolve,如果参数中 promise 有一个失败,此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
Promise.all = function(iterable) {
return new Promise((resolve, reject) => {
let result = []
for(const item of iterable) {
item.then(data => {
result.push(data)
}, reason=> {
result = reason
return
})
}

resolve(result)
})
}

// 下面是测试用例
let p1 = new Promise((resolve, reject) => {
resolve(666)
})

let p2 = new Promise((resolve, reject) => {
resolve(888)
})

let p3 = new Promise((resolve, reject) => {
resolve(999)
})

let p6 = new Promise((resolve, reject) => {
reject(222)
})

let p4 = Promise.all([p1, p2, p3])

p4.then(data => {
console.log(data)
})
//[666, 888, 999]
let p7 = Promise.all([p1, p3, p6])

p7.then(data => {
console.log(data)
})
//222

Promise.race()
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。
Promise.race = function(iterable) {
return new Promise((resolve, reject) => {
for(const item of iterable) {
item.then(data => {
resolve(data)
}, reason=> {
reject(reson)
})
}
})
}
// 测试用例
var p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, ‘one’);
});

var p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, ‘two’);
});

Promise.race([p1, p2]).then(function(value) {
console.log(value);
// Both resolve, but promise2 is faster
});
//two
Promise.resolve()
Promise.resolve = function(data) {
return new Promise((resolve, reject) => {
resolve(data)
})
}

// 测试用例
var p1 = Promise.resolve(123);

p1.then(function(value) {
console.log(value);
});
//123
Promise.reject()
Promise.reject(reason) 方法返回一个带有拒绝原因 reason 参数的 Promise 对象。
Promise.resolve = function(data) {
return new Promise((resolve, reject) => {
reject(data)
})
}

正文完
 0