开玩笑:await vs setImmediate vs useFakeTimers vs new Promise(setImmediate)

Jef*_*ery 5 typescript jestjs setimmediate graphql-subscriptions

以下是 TypeScript 中的 Jest 测试。我想知道为什么需要 setImmediate() 。

第一个例子是一个有效的测试。接下来是我尝试过但不起作用的各种事情。我不明白发生了什么事。pubsub.publish 的签名是: (method) PubSub.publish(triggerName: string, payload: any): Promise<void>

  test.only('subscriptions', async () => {
    const document = parse(`
      subscription {
        create 
      }
    `)

    const sub = <AsyncIterator<ExecutionResult>>await subscribe(schema, document);

    expect(sub.next).toBeDefined()

    // setInterval and process.nextTick also work here:
    setImmediate(() => pubsub.publish('CREATE_ONE', { create: "FLUM!" }))  // this works


    const { value: { errors, data } } = await sub.next()

    expect(errors).toBeUndefined()
    expect(data).toBeDefined()
    expect(data.create).toBe('FLUM!')
  }, 10000)
Run Code Online (Sandbox Code Playgroud)

这些是我尝试过的其他事情,其中​​一些是在研究了类似问题的答案之后做出的。所有这些尝试都会失败,并在测试中出现超时异常:



  test.only('subscriptions', async () => {
  // attempt #1: jest.useFakeTimers()

    const document = parse(`
      subscription {
        create 
      }
    `)

    const sub = <AsyncIterator<ExecutionResult>>await subscribe(schema, document);

    expect(sub.next).toBeDefined()

    // #1, cont: 
    // pubsub.publish('CREATE_ONE', { create: "FLUM!" })
    // or...
    // await pubsub.publish('CREATE_ONE', { create: "FLUM!" })
    // this works, though, like in previous test, but with fake timers:
    // setImmediate(() => pubsub.publish('CREATE_ONE', { create: "FLUM!" }))


    // attempt #2:
    // await pubsub.publish('CREATE_ONE', { create: "FLUM!" })

    // attempt #3:
    // pubsub.publish('CREATE_ONE', { create: "FLUM!" })
    // await new Promise(setImmediate)

    // attempt #3a (variant):
    // await new Promise((resolve) => setImmediate(resolve));

    const { value: { errors, data } } = await sub.next()

    expect(errors).toBeUndefined()
    expect(data).toBeDefined()
    expect(data.create).toBe('FLUM!')
  }, 10000)
Run Code Online (Sandbox Code Playgroud)

据我了解,setImmediate 将一个函数放入事件循环中,以便在任何挂起的 I/O 事件之后立即执行。我不确定为什么需要它,因为 pubsub.publish() 返回一个可以用await处理的Promise ,但在这种情况下发生的情况是下一行,await sub.next()永远不会被调用。

我的想法是,在 pubsub.publish() 中进行了 setInterval 调用,并且 setImmediate 等待任何挂起的 setInterval 事件完成(我对此的理解很模糊)。尝试 3 和 3a 是我在其他地方找到的机制,但它们似乎在这种情况下不起作用。

问题:为什么这个测试需要setImmediate才能通过?

Jef*_*ery 2

所以我的困惑是由于 setImmediate 做什么和不做什么。这就是正在发生的事情:

    // setInterval and process.nextTick also work here:
    setImmediate(() => pubsub.publish('CREATE_ONE', { create: "FLUM!" })) 
    const { value: { errors, data } } = await sub.next()
Run Code Online (Sandbox Code Playgroud)

如果没有 setImmediate(),则在调用 sub.next() 之前发送发布事件,因此不会捕获该事件。您可能认为 setImmediate (或 process.nextTick)会导致立即执行发布函数,但事实并非如此。相反,setImmediate 将发布调用延迟足够长的时间,以便 sub.next() 执行。

我现在要补读一下 setImmediate 和 process.nextTick 的实际工作原理。