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 作用