前端杂谈: 如何实现一个 Promise?
栏目: JavaScript · 发布时间: 5年前
内容简介:关键语句: Promise 是一个在将来某个时刻产生一个单一结果的对象. 通俗一点来说, Promise 代表了一个值, 但是这个值我们并不确定什么时候会被返回.首先, 让我们看看一段最普通的异步代码:按照 Promise 的规范定义, 理想中 Promise 的调用方式为:
A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved (e.g., a network error occurred). A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.
关键语句: Promise 是一个在将来某个时刻产生一个单一结果的对象. 通俗一点来说, Promise 代表了一个值, 但是这个值我们并不确定什么时候会被返回.
A promise is an object that may produce a single value some time in the future.
简单看看 Promise 的历史
-
Promise 在 1980 年代被创建出来
-
在 1988 年正式得名: Promise
-
已经有很多人了解到了 Promise, 但是人们还是坚持使用 node.js 中提倡的以回调函数首个参数传 error 对象的方式处理异步代码.
-
Dojo首次大规模的使用了 Promise , 相应的 Promise/A 被提出用以规范 Promise 的实现
-
JQuery 开始使用 Promise 并真正使 Promise 广为人知
-
JQuery 没有实现部分 Promise 的功能, 这也导致了 Promie/A+ 标准的产生
-
ES6 正式引入了 Promise,并且和已有的实现了 Promise/A 规范的 library 相兼容
实现 Promise 之前, 让我们看看 Promise 有哪些规范
-
Promise 是一个
thenable
对象, 也就是说 Promise 有一个.then()
方法 - 一个 pending 状态的 Promise 可以进入 fulfilled 和 rejected 状态
- promise 一旦进入 fulfilled 或 rejected 状态, 不可再改变其状态
- 一旦 promise 改变了其状态, 它笔芯有一个值(这个值也可能是 undefined)
开始实现一个 Promise
首先, 让我们看看一段最普通的异步代码:
// 异步方法定义 var basicAsyncFunc = function(callback) { setTimeout(function() { var randomNumber = Math.random() if (randomNumber > 0.5) { callback(null, randomNumber) } else { callback(new Error('bad luck...')) } }, 1000) } // 异步方法调用 basicAsyncFunc((err, result) => { if (err) { console.log(`the reason fail is: ${err}`) return } console.log(`success get result: ${result}`) }) 复制代码
按照 Promise 的规范定义, 理想中 Promise 的调用方式为:
// Promise 形式的异步方法定义 var promiseAsyncFunc = function() {} // Promise 形式的异步方法调用 promiseAsyncFunc.then( data => { console.log(`success get result: ${data}`) }, err => { console.log(`the reason fail is: ${err}`) } ) 复制代码
按照这个理想当中的调用方式, 让我们写出第一版代码.
第一版 Promise:能保存回调方法
// Promise 形式的异步方法定义 var promiseAsyncFunc = function() { var fulfillCallback var rejectCallback setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfillCallback(randomNumber) else rejectCallback(randomNumber) }, 1000) return { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } // Promise 形式的异步方法调用 promiseAsyncFunc().then(fulfillCallback, rejectCallback) 复制代码
我们的思路是在 .then()
方法中, 将 fullfill 和 reject 结果的回调函数保存下来, 然后在异步方法中调用. 因为是异步调用, 根据 event-loop 的原理, promiseAsyncFunc().then(fulfillCallback, rejectCallback)
传入的 callback 在异步调用结束时一定是已经赋值过了.
第二版 Promise:实构造函数
当前我们的实现 Promise 中,异步逻辑代码和 Promise 的代码是杂糅在一起的,让我们将其区分开:
var promiseAsyncFunc = function() { var fulfillCallback var rejectCallback return { fulfill: function(value) { if (fulfillCallback && typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject: function(err) { if (rejectCallback && typeof rejectCallback === 'function') { rejectCallback(err) } }, then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } let ownPromise = function(asyncCall) { let promise = promiseAsyncFunc() asyncCall(promise.fulfill, promise.reject) return promise } // Promise 形式的异步方法调用 ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }) 复制代码
我们新定义了一个方法 ownPromise()
用于创建 Promise,并在 promiseAsyncFunc()
中暴露出 fulfill
和 reject
接口方便异步代码去调用。
这里有一个问题,我们在调用 ownPromise()
后得到了 promise 实例,此时我们可以直接调用 fulfill()
, reject()
这两个方法,而理论上我们应该只应暴露 promise 的 then()
方法。所以我们利用闭包将这两个方法隐藏:
var promiseAsyncFunc = function() { var fulfillCallback var rejectCallback return { fulfill: function(value) { if (fulfillCallback && typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject: function(err) { if (rejectCallback && typeof rejectCallback === 'function') { rejectCallback(err) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() asyncCall(defer.fulfill, defer.reject) return defer.promise } // Promise 形式的异步方法调用 ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }) 复制代码
第三版 Promise: 支持状态管理
为了实现规范中对于 Promise 状态变化的要求, 我们需要为 Promise 加入状态管理, 这一步较为简单, 让我们看代码:
const PENDING = Symbol('pending') const FULFILLED = Symbol('fulfilled') const REJECTED = Symbol('rejected') // Promise 形式的异步方法定义 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback return { fulfill: function(value) { if (status !== PENDING) return if (typeof fulfillCallback === 'function') { fulfillCallback(value) status = FULFILLED } }, reject(error) { if (status !== PENDING) return if (typeof rejectCallback === 'function') { rejectCallback(error) status = REJECTED } }, promise: { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() asyncCall(defer.fulfill, defer.reject) return defer.promise } // Promise 形式的异步方法调用 ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }).then(data => console.log(data), err => console.log(err)) 复制代码
这段代码中我们用到了 Symbol 来表示状态常量, 对 Symbol 不了解的同学可以看这里
为了判断 Promise 的状态, 我们加入了 fulfill
和 reject
两个方法。并在其中判断 promise 当前状态。如果不是 pending 状态则直接 return(因为 Promise 状态只可能改变一次)。
现在我们的 promise 实现了对状态控制的规范:
- 只允许改变一次状态
- 只能从 pending => fulfilled 或 pending => rejected
但是我们的 Promise 有一个问题: promise 的值没有被保存下来。如果 promise 在异步调用完成之后才被调用 .then()
方法,则我们无法把异步调用的结果传递给回调函数。为此我们需要为 Promise 加一个 value 字段:
第四版 Promise: 保存异步调用的结果
我们为 promise 加入 value 字段,用于保存 Promise 的执行结果。
// Promise 形式的异步方法定义 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = _value status = FULFILLED if (typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject(error) { if (status !== PENDING) return value = error status = REJECTED if (typeof rejectCallback === 'function') { rejectCallback(error) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } } 复制代码
这里我们又发现一个问题,如果一个 Promise 已经是 fulfill
或 reject
状态。我们再调用 then()
方法时,传入的回调方法永远不会被调用(因为 status 已经不是 pending)。
所以我们需要在 then()
方法中对其状态进行判断:
// Promise 形式的异步方法定义 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = _value status = FULFILLED if (typeof fulfillCallback === 'function') { fulfillCallback(value) } }, reject(error) { if (status !== PENDING) return value = error status = REJECTED if (typeof rejectCallback === 'function') { rejectCallback(error) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { if (status === REJECTED) { _rejectCallback(value) return } if (status === FULFILLED) { _fulfillCallback(value) return } fulfillCallback = _fulfillCallback rejectCallback = _rejectCallback } } } } 复制代码
第五版 Promise: 支持链式调用
为了支持链式调用, .then()
方法的返回值必须是用 thenable
(根据 Promise/A+ 规范, .then()
方法的返回值需要是一个 新
的 Promise)
为此我们加入一个 工具 方法 makeThenable()
。如果传入的 value 本身就有 then()
方法,则直接返回 value。否则返回一个有 then()
方法的对象。
在该对象的 then()
方法中,我们根据 promise 的状态,调用不同的回调方法生成新的 value。
function makeThenable(value, status){ if(value && typeof value.then === 'function'){ return value } if(status === FULFILLED){ return { then: function(fulfillCallback, rejectCallback){ return makeThenable(fulfillCallback(value), FULFILLED) } } } if(status === REJECTED) { return { then: function(fulfillCallback, rejectCallback){ return makeThenable(rejectCallback(value), FULFILLED) } } } } 复制代码
有了以上的 makeThenable()
方法,我们可以在 promise 的 fulfill()
, reject()
回将 value 设置为 thenable
:
var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = makeThenable(_value, FULFILLED) // 保证当前promise的value为 thenable status = FULFILLED if (typeof fulfillCallback === 'function') { value.then(fulfillCallback) } }, reject(error) { if (status !== PENDING) return value = makeThenable(error, REJECTED) 、、 // 保证当前value为 thenable status = REJECTED if (typeof rejectCallback === 'function') { value.then(null, rejectCallback) } }, promise: { then: function(){} } } } 复制代码
接下来让我们看 then()
方法。为了返回一个新的 promise,我们首先得创建一个新的 promise。其次当前 promise 在 fulfill()
或 reject()
时,应该调用新的 promise 的 fullfill()
或 reject()
方法。所以我们在将 fulfullCallback
和 rejectCallback
赋值给当前 promise 时,将其包装一下。代码如下:
promise: { then: function(_fulfillCallback, _rejectCallback) { let newPromiseAsyncFunc = promiseAsyncFunc() let fulfillFunc = function(value) { newPromiseAsyncFunc.fulfill(_fulfillCallback(value)) } let rejectFunc = function(err) { newPromiseAsyncFunc.fulfill(_rejectCallback(err)) } if (status === PENDING) { fulfillCallback = fulfillFunc rejectCallback = rejectFunc } else { value.then(fulfillFunc, rejectFunc) } return newPromiseAsyncFunc.promise } } 复制代码
如此,我们变得到了一个可以链式调用的 promise。让我们来测试一下:
const PENDING = Symbol('pending') const FULFILLED = Symbol('fulfilled') const REJECTED = Symbol('rejected') function makeThenable(value, status) { if (value && typeof value.then === 'function') { return value } if (status === FULFILLED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(fulfillCallback(value), FULFILLED) } } } if (status === REJECTED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(rejectCallback(value), FULFILLED) } } } } // Promise 形式的异步方法定义 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = makeThenable(_value, FULFILLED) status = FULFILLED if (typeof fulfillCallback === 'function') { value.then(fulfillCallback) } }, reject(error) { if (status !== PENDING) return value = makeThenable(error, REJECTED) status = REJECTED if (typeof rejectCallback === 'function') { value.then(null, rejectCallback) } }, promise: { then: function(_fulfillCallback, _rejectCallback) { let newPromiseAsyncFunc = promiseAsyncFunc() let fulfillFunc = function(value) { newPromiseAsyncFunc.fulfill(_fulfillCallback(value)) } let rejectFunc = function(err) { newPromiseAsyncFunc.fulfill(_rejectCallback(err)) } if (status === PENDING) { fulfillCallback = fulfillFunc rejectCallback = rejectFunc } else { value.then(fulfillFunc, rejectFunc) } return newPromiseAsyncFunc.promise } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() asyncCall(defer.fulfill, defer.reject) return defer.promise } let testChainedPromise = ownPromise(function(fulfill, reject) { setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }) .then( data => { console.log(data) return 'return value in then1 fulfill' }, err => { console.log(err) return 'return value in then1 reject' } ) .then( data => { console.log(data) return 'return value in then2 fulfill' }, err => { console.log(err) return 'return value in then2 reject' } ) .then( data => { console.log(data) }, err => { console.log(err) } ) /** console output: 0.9931984611850693 return value in then1 fulfill return value in then2 fulfill */ 复制代码
第六版 Promise: Error handling
这里我们只对 异步调用 和 fulfill 回调 中抛出的 error 进行处理。
首先是 异步
调用部分,我们将其 try catch
起来,在发生异常时调用 reject 方法,并将异常作为参数传入。
let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() try { asyncCall(defer.fulfill, defer.reject) } catch (e) { defer.reject(e) } return defer.promise } 复制代码
然后是 fulfill
中可能出现的异常。我们对 fulfillCallback(value)
可能出现的异常进行捕获,并将异常传递给 rejectCallback
。
function makeThenable(value, status) { if (value && typeof value.then === 'function') { return value } if (status === FULFILLED) { return { then: function(fulfillCallback, rejectCallback) { try { let newValue = fulfillCallback(value) return makeThenable(newValue, FULFILLED) } catch (e) { return makeThenable(rejectCallback(e), FULFILLED) } } } } if (status === REJECTED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(rejectCallback(value), FULFILLED) } } } } 复制代码
最后让我们对完整的代码进行测试:
const PENDING = Symbol('pending') const FULFILLED = Symbol('fulfilled') const REJECTED = Symbol('rejected') function makeThenable(value, status) { if (value && typeof value.then === 'function') { return value } if (status === FULFILLED) { return { then: function(fulfillCallback, rejectCallback) { try { let newValue = fulfillCallback(value) return makeThenable(newValue, FULFILLED) } catch (e) { return makeThenable(rejectCallback(e), FULFILLED) } } } } if (status === REJECTED) { return { then: function(fulfillCallback, rejectCallback) { return makeThenable(rejectCallback(value), FULFILLED) } } } } // Promise 形式的异步方法定义 var promiseAsyncFunc = function() { var status = PENDING var fulfillCallback var rejectCallback var value return { fulfill: function(_value) { if (status !== PENDING) return value = makeThenable(_value, FULFILLED) status = FULFILLED if (typeof fulfillCallback === 'function') { value.then(fulfillCallback) } }, reject(error) { if (status !== PENDING) return value = makeThenable(error, REJECTED) if (typeof rejectCallback === 'function') { value.then(null, rejectCallback) } status = REJECTED }, promise: { then: function(_fulfillCallback, _rejectCallback) { let newPromiseAsyncFunc = promiseAsyncFunc() let fulfillFunc = function(value) { newPromiseAsyncFunc.fulfill(_fulfillCallback(value)) } let rejectFunc = function(err) { newPromiseAsyncFunc.fulfill(_rejectCallback(err)) } if (status === PENDING) { fulfillCallback = fulfillFunc rejectCallback = rejectFunc } else { value.then(fulfillFunc, rejectFunc) } return newPromiseAsyncFunc.promise } } } } let ownPromise = function(asyncCall) { let defer = promiseAsyncFunc() try { asyncCall(defer.fulfill, defer.reject) } catch (e) { defer.reject(e) } return defer.promise } let testChainedPromise = ownPromise(function(fulfill, reject) { throw Error('here is an error in asyncCall') setTimeout(() => { var randomNumber = Math.random() if (randomNumber > 0.5) fulfill(randomNumber) else reject(randomNumber) }, 1000) }) .then( data => { console.log(data) return 'return value in then1 fulfill' }, err => { console.log(err.message) return 'return value in then1 reject' } ) .then( data => { console.log(data) throw Error('here is an error in fulfill1') return 'return value in then2 fulfill' }, err => { console.log(err.message) return 'return value in then2 reject' } ) .then( data => { console.log(data) }, err => { console.log(err.message) } ) // console out: Error: here is an error in asyncCall return value in then1 reject Error: here is an error in fulfill1 return value in then2 reject 复制代码
总结
以上就是我们对于 Promise 的一个简单的实现,实现思路主要参考了 Q-A promise library for javascript 。该实现的 Promise 功能较为简陋,仅实现了部分 api/规范。有任何意见和建议欢迎在评论区交流 ;)
以上所述就是小编给大家介绍的《前端杂谈: 如何实现一个 Promise?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
HTTP Essentials
Stephen A. Thomas、Stephen Thomas / Wiley / 2001-03-08 / USD 34.99
The first complete reference guide to the essential Web protocol As applications and services converge and Web technologies not only assume HTTP but require developers to manipulate it, it is be......一起来看看 《HTTP Essentials》 这本书的介绍吧!