promise、async、await、execution order
11587 ワード
async
can be transformed to promise
.So,if we want to understand async
,we have to understand promise
first.Promise
Normally、
promise
is easury to understand、especially when using like this:promise
.then(() => {
//
})
.then(() => {
//
})
.then(() => {
//
})
then
after then
would make the order of async calbacks clear.Actualy we shoudn't rely on async calbacks order.But a certain exffection order would make me feel more compfortable.Howerse.RESOLVE
and Promise.resolve()
Normally、I initialize a promise by Promise.resolve()
because it seems toroublesome to use Promise
construct like below which I caled it RESOLVE
in this artic.new Promise((resolve,reject)=>{
resolve()
})
Also,I would use promise
in this artic le 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 mater which one you chose.RESOLVE(non-thenable)
or Promise.resolve(non-thenable)
.However,when it commes to
thenable
,things are different.Promise.resolve(thenable)
is not equivalent to RESOLVE(thenable)
new Promise((resolve, reject) => {
resolve(thenable)
})
I explined it carefully in What's the difference between reolve(promise)and rerove('non-thenable-object')?And here is the conclusion:non-thenable
、Promise.resolve(non-thenable)
is equivalent to RESOLVE(non-thenable)
thenable
、Promise.resolve(thenable)
is not equivalent to RESOLVE(thenable)
because RESOLVE(thenable)
new Promise((resolve, reject) => {
resolve(thenable)
})
is equivalent tonew Promise((resolve, reject) => {
Promise.resolve().then(() => {
thenable.then(resolve)
})
})
accoding to spec.It's oviously 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
whilelet 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 cores another question.When would we use Promise.resolve(thenable)
or RESOLVE(thenable)
?It doesn't seem to be that comon.はい、indeed.Except
async
and await
.async
and await
As we all know or spec says the return of async
returns promise.For example:(async function(){}()).toString() //"[object Promise]"
And await
can be used in async
.await
So,how does await
work in async
?Acctording to spec:Await:We can tranform
await
codeconst p1 = Promise.resolve(1)
const async1 = async function() {
const res1 = await p1
console.log(res1)
}
async1()
p1.then(() => console.log('after gen'))
ト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 reult is the same on chrome 70:after gen
1
However,in chrome canary 73 the former reult is
1
after gen
Why
The reason can be found inhttps://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 shound be tranformed toconst 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 get1
after gen
That's the spec change for
await
.Hope you both understand the way before and after change.async
Now,let's talk about async
.How does async
work?Acctording to spec:The spawn used in the above desugaring is a call to the following algorithm.This algorithm.This algorithm does not need to be exposed directly as API to user code,it is parts of the semanic function.
And the
spawn
isfunction 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 Promise.resolve(next.value)
instead of RESOLVE(next.value)
to transform await
.So,I thought the old version or version or version apped to chrome 70 shrome shop befunction 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 compring the relt 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'))
withconst 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:spawn
,you will get the different relt.While you will get the same result with the latter spawn
.spawn
,you will get the same relt.While you will get the different the refth the latter spawn
.async
As 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 returns2
3
1
That's corect.Because spec:Runtime Semantics:EveraluteBody uses
RESOLVE
in async
implemenation.So,why not using
Promise.resolve()
in async
implementation like await
?@MayaLekova explined inhttps://github.com/tc39/ecma2... を選択します.Deferring the implicit creation of the wrapper promise inside
async
functions in case we actually need to await on an asynchrous task(which excludes asynctions without)await
or withawait
s only on reved promises)will indeed remove the performance overhead of turning a synchronous function to asynchronous.It will though introde the possibility to create starvation,for instance the instation.Second,if we want to remove the extra tick for chaining native,non-patch ed promises,this will effectively change overble behaviour for appinations already with provent.Think.infone.ink.ink.info.which will actually introduch Frther inconsistency.
So,in current situation,we can only tranform 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 toconst 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 implemenation or spec of async
doesn't change,we may need to avoid return promise in async
expect you really know's happening.In the case above,if You really want to avoid the delay,you shound avoid using
async
.You can do 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 toconst 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
.コンサート
I hope you can understand
async
,await
and promise
execution order now.When talking about therswords,the most importh thing is to figure out which resove it shound use.RESOLVE
or Promise.resolve()
?RESOLVE(thenable)
is different from Promise.resolve(thenable)