首页 > 解决方案 > 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开始?

标签: javascriptasync-awaitpromise

解决方案


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
]

注意大写AsyncAwait。这些是我们自己制作的简单功能 -

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

展开下面的代码片段来验证我们自制AsyncAwait以下的行为——

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)

有关误用asyncand的更多信息await,请参阅此相关问答


推荐阅读