javascript - Javascript嵌套异步调用执行顺序
问题描述
我无法理解异步代码如何在 javascript 中运行。
我有一个类似于以下的代码:
const start = name => console.log(`${name} started`);
const finish = name => console.log(`${name} finished`);
const wrap = async (promise, name) => {
start(name);
const promiseResult = await promise;
finish(name);
return promiseResult;
}
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const inner = async ms => {
return await sleep(1000);
}
const outer = async ms => {
await wrap(inner(ms), 'inner1');
await wrap(inner(ms), 'inner2');
await wrap(inner(ms), 'inner3');
}
const testPromise = async promise => {
const t0 = performance.now();
const promiseResult = await promise;
const t1 = performance.now();
console.log(`Running promise took ${t1 - t0} milliseconds`);
return promiseResult;
}
testPromise(wrap(outer(5000), 'outer'));
上述代码的输出是:
inner1 started
outer started
inner1 finished
inner2 started
inner2 finished
inner3 started
inner3 finished
outer finished
Running promise took 3026.2199999997392 milliseconds
正如你在输出中看到的,inner1
在开始之前outer
就开始了,这很奇怪!我期望的是所有inner
调用都在outer
.
我在谷歌上做了很多研究,但不幸的是找不到任何有用的东西。
对我有用的是显式模拟调用wrap
函数,outer
如下所示:
const start = name => console.log(`${name} started`);
const finish = name => console.log(`${name} finished`);
const wrap = async (promise, name) => {
start(name);
const promiseResult = await promise;
finish(name);
return promiseResult;
}
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const inner = async ms => {
return await sleep(1000);
}
const outer = async ms => {
await wrap(inner(ms), 'inner1');
await wrap(inner(ms), 'inner2');
await wrap(inner(ms), 'inner3');
}
const testPromise = async () => {
const t0 = performance.now();
const outerName = 'outer'; // -- emulate calling `await wrap(outer(5000), 'outer')`
start(outerName); // --
const promiseResult = await outer(5000); // --
finish(outerName); // -- finished emulation of `wrap`
const t1 = performance.now();
console.log(`Running promise took ${t1 - t0} milliseconds`);
return promiseResult;
}
testPromise();
上面代码的输出是我真正期望的:
outer started
inner1 started
inner1 finished
inner2 started
inner2 finished
inner3 started
inner3 finished
outer finished
Running promise took 3155.5249999510124 milliseconds
我做错了什么使inner1
开始之前outer
开始?
解决方案
async
您的问题表明了对有效使用和的一些误解await
-
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
async function wrap (p, label) {
console.log("started", label)
const t = Date.now()
const result = await p
console.log("finished", label)
return { result, delta: Date.now() - t }
}
async function inner () {
await sleep(1000)
return Math.floor(Math.random() * 100)
}
async function outer () {
const a = await wrap(inner(), "inner1")
const b = await wrap(inner(), "inner2")
const c = await wrap(inner(), "inner3")
return [a, b, c]
}
wrap(outer(), "outer")
.then(JSON.stringify)
.then(console.log, console.error)
started inner1
started outer
finished inner1
started inner2
finished inner2
started inner3
finished inner3
finished outer
{"result":[{"result":58,"delta":1004},{"result":58,"delta":1001},{"result":67,"delta":1000}],"delta":3009}
async 和 await 并不特别
这是一个有用的练习来想象它并不存在,你必须自己发明它们async
-await
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
function* pick1 () {
yield Await(sleep(1000))
return Math.random()
}
function* pick3 () {
const a = yield Await(pick1())
console.log("first", a)
const b = yield Await(pick1())
console.log("second", b)
const c = yield Await(pick1())
console.log("third", c)
return [a, b, c]
}
Async(pick3()).then(console.log, console.error)
first 0.22559836642959197
second 0.41608184867397835
third 0.3789851899519072
[
0.22559836642959197,
0.41608184867397835,
0.3789851899519072
]
注意大写Async
和Await
。这些是我们自己制作的简单功能 -
const isGenerator = x =>
x?.constructor == (function*(){}()).constructor
const Await = x =>
isGenerator(x) ? Async(x) : Promise.resolve(x)
function Async (it) {
return new Promise((resolve, reject) => {
function next (x) {
const {value, done} = it.next(x)
return done
? resolve(value)
: value.then(next, reject)
}
next()
})
}
async
希望这可以帮助您了解和的幕后发生的事情await
。替换您可以自己编写的程序只不过是一点语法糖:D
展开下面的代码片段来验证我们自制Async
及Await
以下的行为——
const isGenerator = x =>
x?.constructor == (function*(){}()).constructor
const Await = x =>
isGenerator(x) ? Async(x) : Promise.resolve(x)
function Async (it) {
return new Promise((resolve, reject) => {
function next (x) {
const {value, done} = it.next(x)
return done
? resolve(value)
: value.then(next, reject)
}
next()
})
}
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
function* pick1 () {
yield Await(sleep(1000))
return Math.random()
}
function* pick3 () {
const a = yield Await(pick1())
console.log("first", a)
const b = yield Await(pick1())
console.log("second", b)
const c = yield Await(pick1())
console.log("third", c)
return [a, b, c]
}
Async(pick3()).then(console.log, console.error)
有关误用async
and的更多信息await
,请参阅此相关问答
推荐阅读
- sed - sed命令将逗号分隔的字符串分成一定的长度
- node.js - 会话实体在对话流的用户实体解析中的使用
- c# - 将空字段从数据表写入 xml 文件
- java - 如何在 Jetty 中添加重试到端口绑定
- laravel - Laravel:1060 运行迁移时列名重复
- c# - 使用委托和事件在 Unity 中创建事件管理器(消息系统)
- powershell - 如何在 PowerShell 中转义和替换“\”
- react-native - react-native 0.57.4,`_typeof3 is an instance of Object`错误
- google-bigquery - 谷歌bigquery中多个表的原子加载
- groovy - 无法在 Groovyshell 上应用 Newify