摘要:
async can be transformed to promise. So, if we want to understand async, we have to understand promise first.
PromiseNormally, promise is easy to understand, especially when using like this:
promise .then(() => { // }) .then(() => { // }) .then(() => { // })
then after then would make the order of async callbacks clear. Actually we shouldn"t rely on async callbacks order. But a certain execution order would make me feel more comfortable. However, sometimes, things are different.
RESOLVE and Promise.resolve()Normally, I initialize a promise by Promise.resolve() because it seems too troublesome to use Promise constructor like below which I called it RESOLVE in this article.
new Promise((resolve,reject)=>{ resolve() })
Also, I would use promise in this article for reference to thenable. Because in most cases, we uses promise and promise is the subset of thenable.
Normally, I used it by Promise.resolve(non-thenable) which is equivalent to RESOLVE(non-thenable)
new Promise((resolve, reject) => { resolve(non-thenable) })
So, it doesn"t matter which one you choose. RESOLVE(non-thenable) or Promise.resolve(non-thenable).
However, when it comes to thenable, things are different. Promise.resolve(thenable) is not equivalent to RESOLVE(thenable)
new Promise((resolve, reject) => { resolve(thenable) })
I explained it carefully in What"s the difference between resolve(promise) and resolve("non-thenable-object")?. And here is the conclusion:
for non-thenable, Promise.resolve(non-thenable) is equivalent to RESOLVE(non-thenable)
for thenable, Promise.resolve(thenable) is not equivalent to RESOLVE(thenable) because RESOLVE(thenable)
new Promise((resolve, reject) => { resolve(thenable) })
is equivalent to
new Promise((resolve, reject) => { Promise.resolve().then(() => { thenable.then(resolve) }) })
according to spec. It"s obviously not equivalent to Promise.resolve(thenable). You can test it by this example:
let p1 = Promise.resolve(1) Promise.resolve(p1).then(res => { console.log(res) }) p1.then(res => { console.log(2) }) //1 //2
while
let p1 = Promise.resolve(1) new Promise((resolve, reject) => { resolve(p1) }).then(res => { console.log(res) }) p1.then(res => { console.log(2) }) //2 //1
So, here comes another question. When would we use Promise.resolve(thenable) or RESOLVE(thenable)? It doesn"t seem to be that common.
Yes, indeed. Except async and await.
async and awaitAs we all know or spec says that the result of async returns promise. For example:
(async function(){}()).toString() //"[object Promise]"
And await can be used in async.
awaitSo, how does await work in async? According to spec:Await:
We can transform await code
const p1 = Promise.resolve(1) const async1 = async function() { const res1 = await p1 console.log(res1) } async1() p1.then(() => console.log("after gen"))
to
const p1 = Promise.resolve(1) const async1 = async function() { new Promise(resolve => { resolve(p1) }).then(res => { const res1 = res console.log(res1) }) } async1() p1.then(() => console.log("after gen"))
The result is the same on chrome 70:
after gen
1
However, in chrome canary 73 the former result is
1
after gen
Why?
The reason can be found in https://github.com/tc39/ecma2... Simply say, the spec to await was going to change to:
What"s difference?
The difference is exactly the difference between RESOLVE(thenable) and Promise.resolve(thenable).
In the past and chrome 70, we are using RESOLVE(thenable), so I transformed the code like above. If using this new spec, the code should be transformed to
const p1 = Promise.resolve(1) const async1 = async function() { Promise.resolve(p1).then(res => { const res1 = res console.log(res1) }) } async1() p1.then(() => console.log("after gen"))
In this case, chrome 70 and canary 73 would all get
1
after gen
That"s the spec change for await. Hope you both understand the way before and after change.
asyncNow, let"s talk about async. How does async work? According to spec:
The spawn used in the above desugaring is a call to the following algorithm. This algorithm does not need to be exposed directly as an API to user code, it is part of the semantics of async functions.
And the spawn is
function spawn (genF, self) { return new Promise(function (resolve, reject) { var gen = genF.call(self) function step (nextF) { var next try { next = nextF() } catch (e) { // finished with failure, reject the promise reject(e) return } if (next.done) { // finished with success, resolve the promise resolve(next.value) return } // not finished, chain off the yielded promise and `step` again Promise.resolve(next.value).then( function (v) { step(function () { return gen.next(v) }) }, function (e) { step(function () { return gen.throw(e) }) } ) } step(function () { return gen.next(undefined) }) }) }
However, I think the spawn is the future version which doesn"t apply to chrome 70 because it used Promise.resolve(next.value) instead of RESOLVE(next.value) to transform await. So, I thought the old version or version applied to chrome 70 should be
function spawn (genF, self) { return new Promise(function (resolve, reject) { var gen = genF.call(self) function step (nextF) { var next try { next = nextF() } catch (e) { // finished with failure, reject the promise reject(e) return } if (next.done) { // finished with success, resolve the promise resolve(next.value) return } // not finished, chain off the yielded promise and `step` again /* modified line */ new Promise(resolve => resolve(next.value)).then( /* origin line */ // Promise.resolve(next.value).then( function (v) { step(function () { return gen.next(v) }) }, function (e) { step(function () { return gen.throw(e) }) } ) } step(function () { return gen.next(undefined) }) }) }
You can tested it by comparing the result of below example.
const p1 = Promise.resolve(1) const async1 = async function () { const res1 = await p1 console.log(res1) } async1() p1.then(() => console.log("after gen"))
with
const p1 = Promise.resolve(1) const gen = function* () { const res1 = yield p1 console.log(res1) } const async1Eq = function () { spawn(gen, this) } async1Eq() p1.then(() => console.log("after gen"))
The result would be:
On chrome 70, with the former spawn, you will get the different result. While you will get the same result with the latter spawn.
In the same way, on chrome 73, with the former spawn, you will get the same result. While you will get the different result with the latter spawn.
Problems in asyncAs async return value is using a RESOLVE, so it might be a little delay when return promise in async function body. For example:
const p1 = Promise.resolve(1) const async1 = async () => { return p1 } async1().then(res => { console.log(res) }) p1.then(res => { console.log(2) }).then(res => { console.log(3) })
chrome 70 ,73 all returns
2
3
1
That"s correct. Because spec: Runtime Semantics: EvaluateBody uses RESOLVE in async implementation.
So, why not using Promise.resolve() in async implementation like await? @MayaLekova explained in https://github.com/tc39/ecma2... ,
Deferring the implicit creation of the wrapper promise inside async functions in case we actually need to await on an asynchronous task (which excludes async functions without await or with awaits only on resolved promises) will indeed remove the performance overhead of turning a synchronous function to asynchronous. It will though introduce the possibility to create starvation, for instance if the await statement is in a loop. This possibility outweighs the performance gain IMO, so it"s better not to do it.Second, if we want to remove the extra tick for chaining native, non-patched promises (introduce "eager" chaining), this will effectively change observable behaviour for applications already shipping with native promises. Think of it as changing the behaviour of line 2 from above to be equivalent to line 3, which will actually introduce further inconsistency.
So, in current situation, we can only transform code above by RESOLVE.
const p1 = Promise.resolve(1) const async1 = () => { return new Promise(resolve => { resolve(p1) }) } async1().then(res => { console.log(res) }) p1.then(res => { console.log(2) }).then(res => { console.log(3) })
which is equivalent to
const p1 = Promise.resolve(1) const async1 = () => { return new Promise(resolve => { return Promise.resolve().then(() => { p1.then(resolve) }) }) } async1().then(res => { console.log(res) }) p1.then(res => { console.log(2) }).then(res => { console.log(3) })
So, if implementation or spec of async doesn"t change, we may need to avoid return promise in async expect you really know what"s happening.
In the case above, if you really want to avoid the delay, you should avoid using async. You can do it by:
const p1 = Promise.resolve(1) const async1 = () => { return p1 } async1().then(res => { console.log(res) }) p1.then(res => { console.log(2) }).then(res => { console.log(3) })
which can be written to
const p1 = Promise.resolve(1) p1.then(res => { console.log(res) }) p1.then(res => { console.log(2) }).then(res => { console.log(3) })
That"s why I love promise instead of async.
ConclusionI hope you can understand async, await and promise execution order now. When talking about these words, the most important thing is to figure out which resolve it should use.
RESOLVE or Promise.resolve()? RESOLVE(thenable) is different from Promise.resolve(thenable)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/100484.html
摘要:控制臺(tái)將顯示回調(diào)地獄通常,回調(diào)只能由一個(gè)異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡(jiǎn)化異步編碼旅程異步編程是一項(xiàng)在中無法避免的挑戰(zhàn)。 JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化? 請(qǐng)思考以下代碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數(shù)語言都處理每...
摘要:控制臺(tái)將顯示回調(diào)地獄通常,回調(diào)只能由一個(gè)異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡(jiǎn)化異步編碼旅程異步編程是一項(xiàng)在中無法避免的挑戰(zhàn)。 JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化? 請(qǐng)思考以下代碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數(shù)語言都處理每...
摘要:控制臺(tái)將顯示回調(diào)地獄通常,回調(diào)只能由一個(gè)異步函數(shù)調(diào)用。更多資源使更友好規(guī)范使用異步函數(shù)簡(jiǎn)化異步編碼旅程異步編程是一項(xiàng)在中無法避免的挑戰(zhàn)。 JavaScript經(jīng)常聲稱是_異步_。那是什么意思?它如何影響發(fā)展?近年來這種方法有何變化? 請(qǐng)思考以下代碼: result1 = doSomething1(); result2 = doSomething2(result1); 大多數(shù)語言都處理每...
摘要:事件驅(qū)動(dòng)機(jī)制的最簡(jiǎn)單形式,是在中十分流行的回調(diào)函數(shù),例如。在回調(diào)函數(shù)這種形式中,事件每被觸發(fā)一次,回調(diào)就會(huì)被觸發(fā)一次。回調(diào)函數(shù)需要作為宿主函數(shù)的一個(gè)參數(shù)進(jìn)行傳遞多個(gè)宿主回調(diào)進(jìn)行嵌套就形成了回調(diào)地獄,而且錯(cuò)誤和成功都只能在其中進(jìn)行處理。 學(xué)習(xí) Node.js 一定要理解的內(nèi)容之一,文中主要涉及到了 EventEmitter 的使用和一些異步情況的處理,比較偏基礎(chǔ),值得一讀。 閱讀原文 大...
摘要:回調(diào)方式將回調(diào)函數(shù)作為參數(shù)傳遞給主函數(shù),同時(shí)在主函數(shù)內(nèi)部處理錯(cuò)誤信息。模塊是促進(jìn)中對(duì)象之間交流的模塊,它是異步事件驅(qū)動(dòng)機(jī)制的核心。在異步函數(shù)的回調(diào)中,根據(jù)執(zhí)行情況觸發(fā)或者事件。比如,當(dāng)異常事件觸發(fā)關(guān)閉數(shù)據(jù)庫的動(dòng)作時(shí)。 原文鏈接:Understanding Nodejs Event-driven Architecture 作者:Samer Buna 翻譯:野草 本文首發(fā)于前端早讀課【...
閱讀 1858·2021-11-25 09:43
閱讀 3692·2021-11-24 10:32
閱讀 1083·2021-10-13 09:39
閱讀 2337·2021-09-10 11:24
閱讀 3351·2021-07-25 21:37
閱讀 3470·2019-08-30 15:56
閱讀 864·2019-08-30 15:44
閱讀 1454·2019-08-30 13:18