优秀的编程知识分享平台

网站首页 > 技术文章 正文

从零开始,自己动手实现一个Promise代理对象

nanyue 2024-09-03 16:38:46 技术文章 7 ℃

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的实现

  1. 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)
 })
}

Tags:

最近发表
标签列表