@/utils#isAsyncFunction TypeScript Examples

The following examples show how to use @/utils#isAsyncFunction. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Flow.ts    From sandstone with MIT License 6 votes vote down vote up
private _while = <R extends void | Promise<void>>(condition: ConditionClass | CombinedConditions, callback: () => R, type: 'while' | 'do_while'): R => {
    if (!isAsyncFunction(callback)) {
      this.flowStatement(callback, {
        callbackName: type,
        initialCondition: type === 'while',
        loopCondition: true,
        condition,
      })

      return undefined as any
    }

    const { currentFunction: parentFunction } = this.datapack

    return {
      then: async (onfulfilled: () => void) => {
        // In theory, we are already in the parent function so we shouldn't need to go back in it.

        // Run the previous code
        await this.flowStatementAsync(callback, {
          callbackName: type,
          initialCondition: type === 'while',
          loopCondition: true,
          condition,
        })

        // Go back in the parent function, because we don't know where the last code ended up.
        this.datapack.currentFunction = parentFunction

        // Finally enter the callback function
        this.datapack.createEnterChildFunction(ASYNC_CALLBACK_NAME)
        return onfulfilled?.()
      },
    } as PromiseLike<void> as any
  }
Example #2
Source File: Flow.ts    From sandstone with MIT License 6 votes vote down vote up
forRange = <R extends void | Promise<void>>(from: Score | number, to: Score | number, callback: (score: Score) => R) => {
    const scoreTracker = from instanceof Score ? from : this.datapack.Variable(from)

    // Small optimization: if we know the loop will run at least once, use a do while
    let loop = this.while
    if (typeof from === 'number' && typeof to === 'number' && to > from) {
      loop = this.doWhile
    }

    if (!isAsyncFunction(callback)) {
      return loop(scoreTracker.lowerThan(to), () => {
        callback(scoreTracker)
        scoreTracker.add(1)
      })
    }

    return loop(scoreTracker.lowerThan(to), async () => {
      await callback(scoreTracker)
      scoreTracker.add(1)
    })
  }
Example #3
Source File: Flow.ts    From sandstone with MIT License 6 votes vote down vote up
forScore = <R extends void | Promise<void>>(
    score: Score | number,
    // eslint-disable-next-line no-shadow
    condition: ((score: Score) => ConditionType) | ConditionType,
    // eslint-disable-next-line no-shadow
    modifier: (score: Score) => void,
    // eslint-disable-next-line no-shadow
    callback: (score: Score) => R,
  ): R => {
    const realScore = score instanceof Score ? score : this.datapack.Variable(score)
    const realCondition = typeof condition === 'function' ? condition(realScore) : condition

    if (!isAsyncFunction(callback)) {
      return this.while(realCondition, () => {
        callback(realScore)
        modifier(realScore)
      }) as any
    }

    return this.while(realCondition, async () => {
      await callback(realScore)
      modifier(realScore)
    }) as any
  }
Example #4
Source File: Flow.ts    From sandstone with MIT License 5 votes vote down vote up
if = <R extends void | Promise<void>>(condition: ConditionType, callback: () => R): (R extends void ? ElifElseFlow<R> : ElifElseFlow<R> & PromiseLike<void>) => {
    const ifScore = getConditionScore(this.commandsRoot.Datapack)

    if (!isAsyncFunction(callback)) {
      // /!\ Complicated stuff happening here.
      let callbackFunction: FunctionResource
      const { elseIf: realElseIf, else: realElse } = this.if_(condition, () => { callbackFunction = this.datapack.currentFunction!; callback() }, 'if', ifScore, false)
      const { currentFunction } = this.datapack

      // for Typescript
      if (!currentFunction?.isResource) { throw new Error('Impossible') }

      const ifCommandIndex = currentFunction.commands.length - 1

      const switchToComplicatedIf = () => {
        const command = currentFunction.commands[ifCommandIndex]

        try {
          // If this doesn't raise an error, it means the function didn't get inlined
          this.datapack.resources.getResource(callbackFunction.path, 'functions')

          // The function wasn't inlined - add the '/scoreboard players set' at the end of the function
          if (!callbackFunction?.isResource) { throw new Error('Impossible') }
          callbackFunction.commands.push(['scoreboard', 'players', 'set', ifScore, 1])
        } catch (e) {
          // The function was inlined - add the 'store success' part to the execute
          currentFunction.commands[ifCommandIndex] = ['execute', 'store', 'success', 'score', ifScore, ...command.slice(1)]
        }

        // Add the reset
        currentFunction.commands = [
          ...currentFunction.commands.slice(0, ifCommandIndex),
          ['scoreboard', 'players', 'reset', ifScore],
          ...currentFunction.commands.slice(ifCommandIndex),
        ]
      }

      return {
        elseIf: (...args: Parameters<typeof realElseIf>) => {
          switchToComplicatedIf()
          return realElseIf(...args)
        },
        else: (cb: Parameters<typeof realElse>['0']) => {
          switchToComplicatedIf()
          return realElse(cb)
        },
      } as ElifElseFlow<void> as any
    }

    // First, specify the `if` didn't pass yet (it's in order to chain elif/else)
    ifScore.reset()

    // Async function
    return this.if_(condition, async () => {
      const returnedPromise = callback()
      ifScore.set(1)
      await returnedPromise
    }, 'if', ifScore) as any
  }
Example #5
Source File: Flow.ts    From sandstone with MIT License 4 votes vote down vote up
private if_ = <R extends void | Promise<void>>(
    condition: ConditionType,
    callback: () => R,
    callbackName: string,
    ifScore: Score,
    forceInlineScore = false,
  ): (R extends void ? ElifElseFlow<R> : ElifElseFlow<R> & PromiseLike<void>) => {
    function ensureConsistency(nextCallback: () => void) {
      if (!isAsyncFunction(callback) && isAsyncFunction(nextCallback)) {
        throw new Error('Passed an asynchronous callback in a synchronous if/else if/else. If/else if/else must be all synchronous, or all asynchronous.')
      }
      if (isAsyncFunction(callback) && !isAsyncFunction(nextCallback)) {
        throw new Error('Passed a synchronous callback in an asynchronous if/else if/else. If/else if/else must be all synchronous, or all asynchronous.')
      }
    }

    if (!isAsyncFunction(callback)) {
      // Register the current if
      this.flowStatement(callback, {
        callbackName,
        initialCondition: true,
        loopCondition: false,
        condition,
        forceInlineScore: forceInlineScore ? ifScore : undefined,
      })

      // We know the callback is synchronous. We must prevent the user to pass an asynchronous callback in else/else if.
      return {
        elseIf: (nextCondition: ConditionType, nextCallback: () => void) => {
          // Ensure the callback is synchronous.
          ensureConsistency(nextCallback)

          return this.if_(this.and(this.not(ifScore.matches([0, null])), nextCondition), () => {
            nextCallback()
            ifScore.set(1)
          }, 'else_if', ifScore, true)
        },
        else: (nextCallback: () => void) => {
          // Ensure the callback is synchronous.
          ensureConsistency(nextCallback)

          this.if_(this.not(ifScore.matches([0, null])), nextCallback, 'else', ifScore, false)
        },
      } as ElifElseFlow<void> as any
    }

    const getPreviousPromise = () => this.flowStatementAsync(callback, {
      callbackName,
      initialCondition: true,
      loopCondition: false,
      condition,
    })

    const { currentFunction: parentFunction } = this.datapack

    return {
      elseIf: (nextCondition: ConditionType, nextCallback: () => Promise<void>) => {
        // Ensure the callback is asynchronous.
        ensureConsistency(nextCallback)

        return this.if_(this.and(nextCondition, this.not(ifScore.matches([0, null]))), async () => {
          // We keep the function where the "else if" is running
          const { currentFunction: newCallback } = this.datapack

          // Go back in the parent function
          this.datapack.currentFunction = parentFunction
          // Run the previous "if/else if" code
          await getPreviousPromise()

          // Now, we're going back in the current "else if"
          this.datapack.currentFunction = newCallback

          // First, we run all synchronous code (that will end up in the .mcfunction instantly called by the "else if")
          const returnedPromise = nextCallback()

          // We notice Sandstone that the condition has successfully passed
          ifScore.set(1)

          // Then we run the asynchronous code, that will create other .mcfunction called with /schedule.
          await returnedPromise
        }, 'else_if', ifScore)
      },
      else: (nextCallback: () => Promise<void>) => {
        // Ensure the callback is asynchronous.
        ensureConsistency(nextCallback)

        /*
         * We return the "if" result, which theoritically could allow our users to
         * write `.if().else().if()`, however this is forbidden thanks to our TypeScript types.
         * We have to return the result for the `then` part.
         */
        return this.if_(this.not(ifScore.matches([0, null])), async () => {
          // We keep the function where the "else" is running
          const { currentFunction: newCallback } = this.datapack

          // Go back in the parent function
          this.datapack.currentFunction = parentFunction
          // Run the previous "if"/"else if" code
          await getPreviousPromise()

          // Now, we're going back in the current "else"
          this.datapack.currentFunction = newCallback

          // And we run the "else" code.
          await nextCallback()
        }, 'else', ifScore)
      },
      then: async (onfulfilled: () => void) => {
        // In theory, we are already in the parent function so we shouldn't need to go back in it.

        // Run the previous "if/else if/else" code
        await getPreviousPromise()

        // Go back in the parent function, because we don't know where the last "if/else if/else" code ended up.
        this.datapack.currentFunction = parentFunction

        // Finally enter the callback function
        this.datapack.createEnterChildFunction(ASYNC_CALLBACK_NAME)
        return onfulfilled?.()
      },
    } as any
  }