如何创建自己的 Promise
有没有想过 Promise 在 JavaScript 中是如何工作的? 让我们实现一个,看看我们是否能更好地理解它。
什么是 Promise?
Promise 是一个可以表示异步操作的对象。
该对象承诺返回此异步操作的结果,无论是成功还是失败。 如果此操作从未完成,它将一直等到永恒或直到它被垃圾收集。
我们需要一个媒介来从 Promise 中收集这个异步操作的结果,这就是 then()
方法成为众人瞩目的地方。
Thenable
thenable 是任何具有 then()
方法实现的对象。 是的,Promise 是可以的。
在 Promise 上下文中,then()
方法用于添加处理程序,这些处理程序最终将接收它所表示的异步操作的结果。
Promise
.resolve( 'result' )
.then( successcallback, failurecallback );
完成后的操作会导致 promise 以已解决的值或被拒绝的错误解决。
如果操作成功完成,将触发 successcallback(value)
,并将解析的值作为参数传递。
如果操作严重失败,则触发 failurecallback(error)
并将被拒绝的错误作为参数传递。
因此,Promise 只能具有三种状态来表示异步操作,即:
- pending:等待异步操作完成。
- fulfilled:操作已成功完成。
- rejected:操作以某种方式失败。
当一个 Promise 被实现或被拒绝时,它就成为了 Promise 的最终状态,从而使 Promise 得到了解决,从此幸福地...
一旦 Promise 被解决,就不能对 Promise 的结果或状态进行进一步的更新。
Promise 构造函数:
Promise 构造函数为我们创建了 Promise 对象:
new Promise( executor )
它需要一个将异步活动与 Promise 联系起来的 executor
函数。
这个 executor
函数由 Promise 构造函数调用,参数 resolve 和 reject 是 Promise 构造函数提供的方法,用于执行以下操作:
- resolve(data) :将 Promise 状态设置为已完成,并将结果设置为 data 。
- reject(error) : 将 Promise 状态设置为被拒绝,并将结果设置为 error。
基于异步活动,executor
函数可以调用 resolve 或 reject 方法将结果传递给 Promise。
一个 Promise 一旦被构造,就具有内部属性:
- [[PromiseState]] : promise 的当前状态。
- [[PromiseResult]] :保存已解决的值或被拒绝的原因。
让我们看一个例子:
const error = null ;
//const error = 'error' ; // 更改为真实值以拒绝 promise。
const promise = new Promise( ( resolve, reject ) => {
// 异步操作
setTimeout( () => {
return error ? reject( error ) : resolve( 'final data' ) ;
}, 3000 );
} );
// before 3 seconds:
Promise {<pending>}
[[PromiseState]]: "pending"
[[PromiseResult]]: undefined
//resolved after 3 seconds :
Promise {<fulfilled>: 'final data'}
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: "final data"
//rejected after 3 seconds :
Promise {<rejected>: 'error'}
[[PromiseState]]: "rejected"
[[PromiseResult]]: "error"
创建一个 PromiseCopy:
让我们从构造函数开始。 我们收到 executor
,我们需要传递实用函数的 resolve 和 reject 方法。
resolve 和 reject 是 Promise 中的静态方法,用于创建新的 Promise,如下所示:
Promise.resolve( 'create a resolved promise' );
//Promise {<fulfilled>: 'create a resolved promise'}
Promise.reject( 'create a rejected promise' );
//Promise {<rejected>: 'create a rejected promise'}
当这些方法被传递给 Promise 构造函数中的 executor
函数时,它们的行为会有所不同。 它们绑定到 Promise 实例并返回 undefined 。
有了以上知识,让我们继续创建一个 PromiseCopy:
const states = {
pending: 'PENDING',
fulfilled: 'FULFILLED',
rejected: 'REJECTED'
}
class PromiseCopy{
#state = states.pending // initial state of promise
#result = undefined ;
constructor( executor ){
try{
executor( PromiseCopy.resolve.bind( this ), PromiseCopy.reject.bind( this ) )
}
catch( e ){
PromiseCopy.reject.call( this, e ) ; // reject promise when error thrown.
}
}
static resolve(){ /**/ }
static reject(){ /**/ }
}
Promise.resolve():
Promise.resolve(value)
接收成功响应,该响应设置为 Promise 的#result
。
但是如果这个值是一个 thenable 对象,Promise.resolve()
不会把这个值设置为#result
。
如果将 thenable 对象传递给 Promise.resolve(),它将通过递归调用 Promise.resolve() 来继续解析结果,直到它达到最终值。
一旦 Promise.resolve()
收到最终值,它就会更新 Promise 的 #result
并将状态设置为已完成。
一旦 Promise 完成,我们就可以调用回调函数,急切地等待#result
。 让我们牢记以上几点来编写 resolve()
方法:
class PromiseCopy{
static resolve( value ){
/*
1. 如果直接调用 Promise.resolve,我们返回一个新的 Promise 并将值传递给 resolve 方法。
2. executor 的 resolve 方法在添加此实例的情况下运行相同的代码。
*/
if( !( this instanceof PromiseCopy ) ) return new PromiseCopy( res => res( value ) ) ;
// 如果 promise 已解决,则返回。
if( this.#state !== states.pending )return ;
// 如果 value 是 thenable 对象,我们通过将其传递给 value.then 递归调用 Promise.resolve。
if( isThenable( value ) ) return value.then( PromiseCopy.resolve.bind( this ), PromiseCopy.resolve.bind( this ) );
this.#state = states.fulfilled ;
this.#result = value ;
this.#dispatchCallbacks();
}
#dispatchCallbacks(){ /* */ } ;
}
Promise.reject():
reject(reason)
方法与 resolve()
方法非常相似,只是它不对 thenable 进行任何解包。 它直接前进并在#result
中设置错误原因并将#state
设置为 rejected。 另外,是的,调度回调:
class PromiseCopy{
static reject( reason ){
// 如果直接调用 Promise.reject,我们返回一个新的 Promise 并将原因传递给 reject 方法。
if( !( this instanceof PromiseCopy ) ) return new PromiseCopy( ( _ , rej ) => rej( reason ) ) ;
// 如果 promise 已解决,则返回。
if( this.#state !== states.pending )return ;
this.#state = states.rejected ;
this.#result = reason ;
this.#dispatchCallbacks();
}
#dispatchCallbacks(){ /* */ } ;
}
then() 和 Promise 链接:
一个 Promise then( successcallback, failurecallback )
用于添加回调。这些回调存储在 Promise 实例中,并在 Promise 完成后触发。
Promise.then()
也是可组合的,这意味着我们可以链接 then()
调用,并且每个 then()
方法都会接收上一个异步操作的结果。
这对于避免回调地狱特别有用,因为可以通过 Promise 链接来链接依赖的异步任务。
then( successcallback, failurecallback )
总是返回一个新的 Promise,该 Promise 由 successcallback 或 failurecallback 的返回值解决,具体取决于当前 Promise 的#state
。
如果 successcallback 或 failurecallback 返回一个 Promise,则在执行 Promise 链中的下一个 then()
方法之前,结果会被新 Promise 的 resolve()
方法解包。
另一个重要的事情是,如果这些回调没有通过,当前的 Promise #state
和 #result
会被转发到新的 Promise。
让我们看一下 then()
的实现:
class PromiseCopy{
then( successcallback , failurecallback ){
return new PromiseCopy( ( resolve, reject ) => {
/**
1. handlers 是一个数组,它将存储成功处理程序和错误处理程序。
2. 每个条目将具有:
- 使用resolve值调用的 success 方法。
- 使用rejected值调用的 failure 方法。
*/
this.#handlers.push( {
success( value ){
if( !successcallback )return resolve( value );
try{
resolve( successcallback( value ) );
}
catch( e ){
reject( e )
}
},
fail( error ){
if( !failurecallback )return reject( error );
try{
resolve( failurecallback( error ) );
}
catch( e ){
reject( e )
}
}
} );
// 如果 promise 已经解决,则执行处理程序。
this.#dispatchCallbacks();
});
}
}
请注意上面有一个条目被推送到handlers
,当当前的 Promise 被结算时将被触发。
Handlers
Handlers 是一个数组,其中包含使用 then()
方法附加的回调。 一旦 Promise 被解决,这些处理程序就会被引用并异步触发。
每个处理程序包含方法:
- success() :当 Promise 完成时执行。
- fail() : 当 Promise 被拒绝时执行。
我们可以创建一个 #dispatchCallback()
方法来检查 Promise 是否已结算并最终调用处理程序。
每个处理程序只应在承诺完成后触发一次,之后应从引用中删除。
由于 Promise 处理是在微任务队列中完成的,我们可以使用 queueMicroTask
方法将处理程序作为微任务异步触发。
我们还将有一个私有属性#queued
作为检查以避免重复触发器:
class PromiseCopy{
#handlers = [] ;
#queued = false ;
#dispatchCallbacks(){
if( this.#state === states.pending )return ;
if( this.#queued )return ;
const method = this.#state === states.fulfilled ? 'success' : 'fail' ; //handler method to call
queueMicrotask( () => {
// 如果 promise 被拒绝并且没有可用的处理程序,则记录 promise 错误。
if( this.#state === states.rejected && !this.#handlers.length ) console.error( 'Uncaught (in promisecopy)', this.#result );
for( let handler of this.#handlers ){
handler[ method ]( this.#result ) ;
}
//cleanup activities:
this.#handlers = []; //remove triggered handlers.
this.#queued = false ;
} );
this.#queued = true ;
}
}
Log Promise 错误
如果未提供失败回调来处理错误,则 Promise if denied 会记录一个通用错误。
尽管在 Promise 链中,被拒绝的 Promise 将由处理程序转发,直到它到达没有 #handlers
的 Promise。 这本质上可以是我们在#dispatchCallback()
中触发错误日志的检查。
catch():
我们也可以使用 catch()
方法附加失败回调。 但是,如果 promise 得到满足,我们还需要将该值转发给下一个 then()
方法。
为此,我们可以重用 then()
方法并将 undefined 作为成功回调传递。 相当简单的权利:
class PromiseCopy{
catch( failurecallback ){
return this.then( undefined, failurecallback ) ;
}
}
finally():
finally( finallycallback )
方法无论 Promise 被实现还是被拒绝都会被实现。 但这并不像将 finallycallback 传递给 then( finallycallback, finallycallback )
那样简单。
finally()
的特殊之处在于当前 Promise 的#result
没有传递给 finally 回调。 当前 Promise 的 #state
和 #result
被传递给 finally()
返回的新 Promise。
如果 finallycallback
是异步的,也就是说如果它返回一个 Promise 或 thenable,那么 Promise 链中的下一个回调只有在这个操作完成后才会被触发。
在某种程度上,finallycallback
只是一些在必须成功的 Promise 链之间做的中间任务。
因为如果 finallycallback
有错误,finally()
返回的新 Promise 会因为这个错误而被拒绝。
让我们看一下实现:
class PromiseCopy{
finally( callback ){
// resolveOrReject : forward current #state.
// value : forward current #result.
/** curried function to pass Promise.resolve or
Promise.reject forward based on current Promise #state. **/
const commonCallback = ( resolveOrReject ) => ( value ) => {
const response = callback?.() ;
return Promise.resolve( response ).then( _ => resolveOrReject( value ) );
}
return this.then( commonCallback( Promise.resolve ) , commonCallback( Promise.reject ) );
}
}
总结
在 JavaScript 中实现异步任务时,了解 Promise 的工作原理会非常有益。 我希望这篇文章可以帮助你创建自己的 Promise,然后异步等待它。
相关文章
在 TypeScript 中返回一个 Promise
发布时间:2023/03/19 浏览次数:182 分类:TypeScript
-
本教程讨论如何在 TypeScript 中返回正确的 Promise。这将提供 TypeScript 中 Returns Promise 的完整编码示例,并完整演示每个步骤。
在 JavaScript 中等待 Promise 得到解决
发布时间:2023/03/10 浏览次数:257 分类:JavaScript
-
在本文中,我们将研究如何在执行 JavaScript 中的下一部分代码之前等待 Promise 得到解决。
深度理解 ES6 Promise
发布时间:2023/03/07 浏览次数:138 分类:WEB前端
-
现在我们开始 ES6 深度系列。 如果大家以前从未来到过这里,请从 ES6 教程 开始。 这篇是关于 ES6 中的 Promise API。 如果觉得该动画非常复杂,请继续阅读! Promises 是一个非常复杂的范
如何解决 TypeScript 中 Type 'Promise' is not assignable to type 错误
发布时间:2023/01/31 浏览次数:365 分类:TypeScript
-
当我们尝试将具有 Promise 类型的值分配给具有不兼容类型的值时,会发生 Type Promise is not assignable to type 错误。 要解决错误,需要在赋值之前解决 Promise 并使两个兼容类型的值。 下面是
异步堆栈跟踪:为什么 await 击败了 Promise#then()
发布时间:2023/01/09 浏览次数:71 分类:学无止境
-
与直接使用 promises 相比, async 和 await 不仅可以使开发人员的代码更具可读性它们还可以在 JavaScript 引擎中实现一些有趣的优化! 这篇文章是关于这样一种优化,涉及异步代码的堆栈跟
JavaScript 中检查函数是否返回的 Promise
发布时间:2022/12/15 浏览次数:556 分类:JavaScript
-
要检查函数是否返回 Promise ,请检查函数是否异步或调用它并检查函数是否返回具有函数类型 then 属性的对象。 如果满足任一条件,该函数将返回一个 Promise 。 // ✅ Promise check functio
JavaScript 中 Promise.resolve is not a constructor 错误
发布时间:2022/12/02 浏览次数:212 分类:JavaScript
-
当我们尝试将 Promise.resolve() 方法与 new 运算符一起使用时,会出现Promise.resolve is not a constructor错误。 Promise.resolve() 方法不是构造函数,因此应该在没有 new 运算符的情况下使用它,例如
JavaScript 中如何访问 Promise 的值
发布时间:2022/12/02 浏览次数:75 分类:JavaScript
-
使用 Promise.then() 方法访问承诺的值,例如 p.then(value = console.log(value)) 。 then() 方法接受一个函数,该函数将 promise 的解析值作为参数传递。 // ?️ Example promise const p = Promise . resolve (
JavaScript 中检查函数是否返回 Promise
发布时间:2022/12/02 浏览次数:70 分类:JavaScript
-
要检查函数是否返回 Promise ,请检查函数是否异步或调用它并检查函数是否返回具有函数类型 then 属性的对象。 如果满足任一条件,该函数将返回一个 Promise 。 // ✅ Promise check functio