javascript 作用
One of the most important questions I faced in interviews was how promises are implemented. Since async/await is becoming more popular, you need to understand promises.
我在采访中面临的最重要的问题之一是如何实现承诺。 由于异步/等待变得越来越流行,因此您需要了解Promise。
什么是诺言? (What is a Promise?)
A promise is an object which represents the result of an asynchronous operation which is either resolved or rejected (with a reason).
一个promise是一个对象,它表示异步操作的结果,该结果被解决或被拒绝(有原因)。
There are 3 states
有3个州
Fulfilled:
onFulfilled()
will be called (e.g.,resolve()
was called)已实现:将调用
onFulfilled()
(例如,调用了resolve()
)Rejected:
onRejected()
will be called (e.g.,reject()
was called)拒绝:将调用
onRejected()
(例如,调用了reject()
)Pending: not yet fulfilled or rejected
待处理:尚未实现或拒绝
So let’s see how’s it is implemented:
因此,让我们看看它是如何实现的:
https://github.com/then/promise/blob/master/src/core.js
https://github.com/then/promise/blob/master/src/core.js
According to the definition at Mozilla: It takes an executor function as an argument.
根据Mozilla的定义:它以执行程序函数作为参数。
function noop() {} function Promise(executor) {if (typeof this !== 'object') {throw new TypeError('Promises must be constructed via new');}if (typeof executor !== 'function') {throw new TypeError('Promise constructor\'s argument is not a function');}this._deferredState = 0;this._state = 0;this._value = null;this._deferreds = null;if (executor === noop) return;doResolve(executor, this);
}
Looks like a simple function with some properties initialized to 0
or null
. Here are a few things to notice:
看起来像一个简单的函数,其某些属性初始化为0
或null
。 这里有一些注意事项:
this._state
property can have three possible values as described above:
this._state
属性可以具有三个如上所述的可能值:
0 - pending1 - fulfilled with _value2 - rejected with _value3 - adopted the state of another promise, _value
Its value is0
(pending) when you create a new promise.
创建新的承诺时,其值为0
( 待定) 。
Later doResolve(executor, this)
is invoked with executor and promise
object.
之后, doResolve(executor, this)
与executor and promise
对象一起调用。
Let’s move on to the definition of doResolve
and see how it’s implemented.
让我们继续进行doResolve
的定义,看看它是如何实现的。
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/function doResolve(fn, promise) {var done = false;var resolveCallback = function(value) {if (done) return;done = true;resolve(promise, value);};var rejectCallback = function(reason) {if (done) return;done = true;reject(promise, reason);
};var res = tryCallTwo(fn, resolveCallback, rejectCallback);if (!done && res === IS_ERROR) {done = true;reject(promise, LAST_ERROR);}
}
Here it is again calling tryCallTwo
function with executor and 2 callbacks. The callbacks are again calling resolve
and reject
在这里,它再次使用executor和2个回调调用tryCallTwo
函数。 回调再次调用resolve
和reject
The done
variable is used here to make sure the promise is resolved or rejected only once, so if you try to reject or resolve a promise more than once then it will return because done = true
.
这里, done
变量用于确保仅对诺言进行一次解析或拒绝,因此,如果您多次尝试拒绝或解决诺言,则它将返回,因为done = true
。
function tryCallTwo(fn, a, b) {try {fn(a, b);} catch (ex) {LAST_ERROR = ex;return IS_ERROR;}
}
This function indirectly calls the main executor
callback with 2 arguments. These arguments contain logic on how resolve
or reject
should be called. You can check resolveCallback and rejectCallback in doResolve
function above.
此函数使用2个参数间接调用主executor
回调。 这些参数包含有关如何调用resolve
或reject
逻辑。 您可以在上面的doResolve
函数中检查resolveCallback和rejectCallback 。
If there is an error during execution it will store the error in LAST_ERROR
and return the error.
如果执行期间发生错误,它将错误存储在LAST_ERROR
并返回错误。
Before we jump to the resolve
function definition, let’s check out the .then
function first:
在跳转到resolve
函数定义之前,让我们先检查.then
函数:
Promise.prototype.then = function(onFulfilled, onRejected) {if (this.constructor !== Promise) {return safeThen(this, onFulfilled, onRejected);}var res = new Promise(noop);handle(this, new Handler(onFulfilled, onRejected, res));return res;
};function Handler(onFulfilled, onRejected, promise) {this.onFulfilled = typeof onFulfilled === "function" ? onFulfilled : null;this.onRejected = typeof onRejected === "function" ? onRejected : null;this.promise = promise;
}
So in the above function, then is creating new promise
and assigning it as a property to a new function called Handler
. The Handler
function has arguments onFulfilled and onRejected. Later it will use this promise to resolve or reject with value/reason.
因此,在上述函数中,将创建新的promise
并将其作为属性分配给一个名为Handler
的新函数。 Handler
函数具有onFulfilled和onRejected参数。 稍后,它将使用此承诺以价值/理由来解决或拒绝。
As you can see, the .then
function is calling again another function:
如您所见, .then
函数再次调用另一个函数:
handle(this, new Handler(onFulfilled, onRejected, res));
实现方式: (Implementation:)
function handle(self, deferred) {while (self._state === 3) {self = self._value;}if (Promise._onHandle) {Promise._onHandle(self);}if (self._state === 0) {if (self._deferredState === 0) {self._deferredState = 1;self._deferreds = deferred;return;}if (self._deferredState === 1) {self._deferredState = 2;self._deferreds = [self._deferreds, deferred];return;}self._deferreds.push(deferred);return;}handleResolved(self, deferred);
}
There is a while loop which will keep assigning the resolved promise object to the current promise which is also a promise for
_state === 3
有一个while循环,它将继续将解析的promise对象分配给当前的promise,这也是
_state === 3
的promiseIf
_state = 0(pending)
and promise state has been deferred until another nested promise is resolved, its callback is stored inself._deferreds
如果
_state = 0(pending)
并且承诺状态已推迟到另一个嵌套的承诺被解决,则其回调存储在self._deferreds
function handleResolved(self, deferred) {asap(function() { // asap is external lib used to execute cb immediatelyvar cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;if (cb === null) {if (self._state === 1) {resolve(deferred.promise, self._value);} else {reject(deferred.promise, self._value);}return;}var ret = tryCallOne(cb, self._value);if (ret === IS_ERROR) {reject(deferred.promise, LAST_ERROR);} else {resolve(deferred.promise, ret);}});
}
What's happening:
发生了什么:
If the state is 1
(fulfilled)
then call the resolve else reject如果状态为1
(fulfilled)
则调用解决方法 else 拒绝If
onFulfilled
oronRejected
isnull
or if we used an empty.then()
resolved or reject will be called respectively如果
onFulfilled
或onRejected
为null
或如果我们使用一个空.then()
解析或拒绝将分别被调用If
cb
is not empty then it is calling another functiontryCallOne(cb, self._value)
如果
cb
不为空,则它正在调用另一个函数tryCallOne(cb, self._value)
function tryCallOne(fn, a) {try {return fn(a);} catch (ex) {LAST_ERROR = ex;return IS_ERROR;}
} a) {
tryCallOne
: This function only calls the callback that is passed into the argument self._value
. If there is no error it will resolve the promise, otherwise it will reject it.
tryCallOne
:此函数仅调用传递到参数self._value
的回调。 如果没有错误,它将解决承诺,否则将拒绝它。
Every promise must supply a .then()
method with the following signature:
每个Promise必须提供具有以下签名的.then()
方法:
promise.then(onFulfilled?: Function,onRejected?: Function
) => Promise
Both
onFulfilled()
andonRejected()
are optional.onFulfilled()
和onRejected()
都是可选的。- If the arguments supplied are not functions, they must be ignored. 如果提供的参数不是函数,则必须将其忽略。
onFulfilled()
will be called after the promise is fulfilled, with the promise’s value as the first argument.在实现诺言之后,将调用
onFulfilled()
,并将诺言的值作为第一个参数。onRejected()
will be called after the promise is rejected, with the reason for rejection as the first argument.在拒绝承诺后,将调用
onRejected()
并将拒绝的原因作为第一个参数。Neither
onFulfilled()
noronRejected()
may be called more than once.onFulfilled()
和onRejected()
不得被调用一次以上。.then()
may be called many times on the same promise. In other words, a promise can be used to aggregate callbacks..then()
可能在同一诺言中被多次调用。 换句话说,promise可以用于聚集回调。.then()
must return a new promise..then()
必须返回新的诺言。
承诺链 (Promise Chaining)
.then
should return a promise. That's why we can create a chain of promises like this:
.then
应该兑现承诺。 这就是为什么我们可以创建如下这样的承诺链:
Promise
.then(() => Promise.then(() => Promise.then(result => result)
)).catch(err)
兑现诺言 (Resolving a promise)
Let’s see the resolve
function definition that we left earlier before moving on to .then()
:
让我们看一下在继续.then()
之前我们留下的resolve
函数定义:
function resolve(self, newValue) {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedureif (newValue === self) {return reject(self,new TypeError("A promise cannot be resolved with itself."));}if (newValue &&(typeof newValue === "object" || typeof newValue === "function")) {var then = getThen(newValue);if (then === IS_ERROR) {return reject(self, LAST_ERROR);}if (then === self.then && newValue instanceof Promise) {self._state = 3;self._value = newValue;finale(self);return;} else if (typeof then === "function") {doResolve(then.bind(newValue), self);return;}
}self._state = 1;self._value = newValue;finale(self);
}
We check if the result is a promise or not. If it’s a function, then call that function with value using
doResolve()
.我们检查结果是否是一个承诺。 如果它是一个函数,则使用
doResolve()
以值调用该函数。If the result is a promise then it will be pushed to the
deferreds
array. You can find this logic in thefinale
function.如果结果是一个承诺,那么它将被推送到
deferreds
数组。 您可以在finale
功能中找到此逻辑。
拒绝承诺: (Rejecting a promise:)
Promise.prototype['catch'] = function (onRejected) {return this.then(null, onRejected);
};
The above function can be found in ./es6-extensions.js
.
可以在./es6-extensions.js
找到以上功能。
Whenever we reject a promise, the .catch
callback is called which is a sugar coat for then(null, onRejected)
.
每当我们拒绝承诺时, .catch
调用.catch
回调,这是then(null, onRejected)
。
Here is the basic rough diagram that I have created which is a birds-eye view of what's happening inside:
这是我创建的基本示意图,它是内部情况的鸟瞰图:
Let’s see once again how everything is working:
让我们再次看看一切如何进行:
For example, we have this promise:
例如,我们有以下承诺:
new Promise((resolve, reject) => {setTimeout(() => {resolve("Time is out");}, 3000)
})
.then(console.log.bind(null, 'Promise is fulfilled'))
.catch(console.error.bind(null, 'Something bad happened: '))
Promise
constructor
is called and an instance is created withnew Promise
调用Promise
constructor
,并使用new Promise
创建实例executor
function is passed todoResolve(executor, this)
and callback where we have definedsetTimeout
will be called bytryCallTwo(executor, resolveCallback, rejectCallback)
so it will take 3 seconds to finishexecutor
函数传递给doResolve(executor, this)
和tryCallTwo(executor, resolveCallback, rejectCallback)
将调用我们定义了setTimeout
回调,因此需要3秒钟才能完成We are calling
.then()
over the promise instance so before ourtimeout
is completed or any asyncapi
returns,Promise.prototype.then
will be called as.then(cb, null)
我们在promise实例上调用
.then()
,因此在timeout
或任何异步api
返回之前,Promise.prototype.then
将被称为Promise.prototype.then
.then(cb, null)
.then
creates a newpromise
and passes it as an argument tonew Handler(onFulfilled, onRejected, promise)
.then
创建一个新的promise
并将其作为参数传递给new Handler(onFulfilled, onRejected, promise)
handle
function is called with the originalpromise
instance and thehandler
instance we created in point 4.使用原始
promise
实例和我们在第4点中创建的handler
实例调用handle
函数。Inside the
handle
function, currentself._state = 0
andself._deferredState = 0
soself_deferredState
will become1
andhandler
instance will be assigned toself.deferreds
after that control will return from there里面的
handle
功能,目前self._state = 0
和self._deferredState = 0
这样self_deferredState
将成为1
和handler
实例将被分配到self.deferreds
后控制将回到那里After
.then()
we are calling.catch()
which will internally call.then(null, errorCallback)
— again the same steps are repeated from point 4 to point 6 and skip point 7 since we called.catch
once在
.catch()
之后.then()
我们将调用.catch()
,该方法将在内部调用.then(null, errorCallback)
-再次,从点4到点6重复相同的步骤, 并跳过点7,因为我们一次调用.catch
Current
promise
state is pending and it will wait until it is resolved or rejected. So in this example, after 3 seconds,setTimeout
callback is called and we are resolving this explicitly which will callresolve(value)
.当前的
promise
状态处于挂起状态,它将等待直到解决或拒绝该状态。 因此,在此示例中,在3秒钟后,调用了setTimeout
回调,并且我们正在明确解决此问题,这将调用resolve(value)
。resolveCallback
will be called with valueTime is out
:) and it will call the mainresolve
function which will check ifvalue !== null && value == 'object' && value === 'function'
resolveCallback
值将为Time is out
:),它将调用主resolve
函数,该函数将检查value !== null && value == 'object' && value === 'function'
It will fail in our case since we passed
string
andself._state
will become1
withself._value = 'Time is out'
and laterfinale(self)
is called.因为我们通过它会在我们的案例失败
string
和self._state
将成为1
与self._value = 'Time is out'
,后来finale(self)
被调用。finale
will callhandle(self, self.deferreds)
once becauseself._deferredState = 1
, and for the chain of promises, it will callhandle()
for eachdeferred
function.由于
self._deferredState = 1
,finale
将调用一次handle(self, self.deferreds)
,对于诺言链,它将为每个deferred
函数调用handle()
。In the
handle
function, sincepromise
is resolved already, it will callhandleResolved(self, deferred)
在
handle
函数中,由于promise
已经解决,它将调用handleResolved(self, deferred)
handleResolved
function will check if_state === 1
and assigncb = deferred.onFulfilled
which is ourthen
callback. LatertryCallOne(cb, self._value)
will call that callback and we get the final result. While doing this if any error occurred thenpromise
will be rejected.handleResolved
功能会检查是否_state === 1
和分配cb = deferred.onFulfilled
这是我们then
回调。 稍后tryCallOne(cb, self._value)
将调用该回调,然后得到最终结果。 在执行此操作时,如果发生任何错误,则promise
将被拒绝。
当诺言被拒绝时 (When a promise is rejected)
In this case, all the steps will remain the same — but in point 8 we call reject(reason)
. This will indirectly call rejectCallback
defined in doResolve()
and self._state
will become 2
. In the finale
function cb
will be equal to deferred.onRejected
which will be called later by tryCallOne
. That’s how the .catch
callback will be called.
在这种情况下,所有步骤将保持不变-但在第8点中,我们将其称为reject(reason)
。 这将间接调用rejectCallback
doResolve()
定义的doResolve()
而self._state
将变为2
。 在finale
函数中, cb
等于deferred.onRejected
,稍后将由tryCallOne
。 这就是.catch
回调将被调用的方式。
That's all for now! I hope you enjoyed the article and it helps in your next JavaScript interview.
目前为止就这样了! 我希望您喜欢这篇文章,并且对您下一次JavaScript采访有所帮助。
If you encounter any problem feel free to get in touch or comment below. I would be happy to help ?
如果您遇到任何问题,请在 下面 与我们联系 或发表评论。 我很乐意提供帮助吗?
Don’t hesitate to clap if you considered this a worthwhile read!
如果您认为这值得一读,请随时鼓掌!
Originally published at 101node.io on February 05, 2019.
最初于2019年2月5日发布在101node.io上。
翻译自: https://www.freecodecamp.org/news/how-javascript-promises-actually-work-from-the-inside-out-76698bb7210b/
javascript 作用