Using Function.InvokeAfter() In Power Query

Last year I blogged about how to introduce a delay between web service requests in M code. Since then a new function has been added to Power Query which makes this a lot easier: Function.InvokeAfter(). This function doesn’t seem to be documented anywhere apart from the Power Query language reference (downloadable from here); the signature is as follows:

Function.InvokeAfter(function as function, delay as duration) as any

It invokes a function after waiting for a given amount of time. Here’s a simple example of how it can be used that declares a function which returns the current date/time as text, then calls it twice with a five second delay in between:

let
GetTimeAsText = ()=> DateTime.ToText(DateTime.LocalNow()),
Output = GetTimeAsText() & " "
& Function.InvokeAfter(GetTimeAsText, #duration(0,0,0,5))
in
Output

The output of this query (at the time of writing) is:

28/04/2015 23:06:38 28/04/2015 23:06:43

One thing that did confuse me a bit was the fact that Function.InvokeAfter() doesn’t allow you to pass a list of arguments for the function you’re invoking like Function.Invoke(). The nice people at Microsoft helped me out with this though, and here’s a slightly more complicated example showing how to use Function.InvokeAfter() with a function that appends “Hello “ to a person’s name:

let
SayHello = (personname as text) as text => "Hello " & personname,
Output = Function.InvokeAfter(()=>SayHello("Chris"), #duration(0,0,0,5))
in
Output

36 thoughts on “Using Function.InvokeAfter() In Power Query

    1. Hi Chris, can you give a simple example of how to use this in an query. I’m still hitting the request limit so I’m doing something wrong.

      1. Chris Webb – My name is Chris Webb, and I work on the Fabric CAT team at Microsoft. I blog about Power BI, Power Query, SQL Server Analysis Services, Azure Analysis Services and Excel.
        Chris Webb says:

        It might be easier for you to share the code you are using, so I can see whether you are doing something wrong.

    1. Chris Webb – My name is Chris Webb, and I work on the Fabric CAT team at Microsoft. I blog about Power BI, Power Query, SQL Server Analysis Services, Azure Analysis Services and Excel.
      Chris Webb says:

      That looks very useful, thanks!

    1. Chris Webb – My name is Chris Webb, and I work on the Fabric CAT team at Microsoft. I blog about Power BI, Power Query, SQL Server Analysis Services, Azure Analysis Services and Excel.
      Chris Webb says:

      Thanks!

  1. Hi Chris,

    Can you please provide an sample code on how to use Function.InvokeAfter with Web.Contents. i basically need to query a dynamic web page but wants to wait until all the scripts are done executing before Power Query looks at the DOM. Thanks much

    1. Chris Webb – My name is Chris Webb, and I work on the Fabric CAT team at Microsoft. I blog about Power BI, Power Query, SQL Server Analysis Services, Azure Analysis Services and Excel.
      Chris Webb says:

      If you have a working query, all you need to do is take the step that calls Web.Contents() and convert it to a function. There’s another example of how to use Function.InvokeAfter() here if it helps: https://blog.crossjoin.co.uk/2016/04/04/timing-m-query-execution-in-power-query-and-power-bi-revisited/

  2. Chris can you help me with insert InvokAfter function in below function?

    let
    AddressLookup = (lat as text, long as text) =>
    let
    Source = Json.Document(Web.Contents(“http://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=”&lat&”&lon=”&long&””)),
    address = Source[address],
    #”Converted to Table” = Record.ToTable(address),
    #”Filtered Rows” = Table.SelectRows(#”Converted to Table”, each ([Name] = “city” or [Name] = “country” or [Name] = “house_number” or [Name] = “postcode” or [Name] = “road”)),
    #”Appended Query” = Table.Combine({#”Filtered Rows”, DefaultValues}),
    #”Removed Duplicates” = Table.Distinct(#”Appended Query”, {“Name”}),
    #”Transposed Table” = Table.Transpose(#”Removed Duplicates”),
    #”Promoted Headers” = Table.PromoteHeaders(#”Transposed Table”, [PromoteAllScalars=true]),
    #”Changed Type” = Table.TransformColumnTypes(#”Promoted Headers”,{{“house_number”, Int64.Type}, {“road”, type text}, {“city”, type text}, {“postcode”, type text}, {“country”, type text}})
    in
    #”Changed Type”
    in
    AddressLookup

    1. Chris Webb – My name is Chris Webb, and I work on the Fabric CAT team at Microsoft. I blog about Power BI, Power Query, SQL Server Analysis Services, Azure Analysis Services and Excel.
      Chris Webb says:

      Just replace the Source and address steps with the following three lines:

      Source = ()=>Json.Document(Web.Contents(“http://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=”&lat&”&lon=”&long&””)),
      Delay = Function.InvokeAfter(Source, #duration(0,0,0,5)),
      address = Delay[address],

  3. Hi Chris, I can’t find a way to make this time lag work for the following function. The error I receive is “We cannot convert a value of Type Table to type Function” (fair enough, but I don’t know how to fix it).

    Code for the GetData query:
    let GetResults=(URL) =>
    let
    Source = Excel.Workbook(Web.Contents(URL), null, true),
    #”Exclusion Request_Sheet” = Source{[Item=”Exclusion Request”,Kind=”Sheet”]}[Data],
    #”Changed Type” = Table.TransformColumnTypes(#”Exclusion Request_Sheet”,{{“Column1”, type any}, {“Column2”, type text}, {“Column3”, type any}, {“Column4”, type any}, {“Column5”, type text}, {“Column6”, type any}, {“Column7”, type text}, {“Column8”, type any}, {“Column9”, type any}, {“Column10”, type any}, {“Column11”, type any}, {“Column12”, type text}, {“Column13”, type any}, {“Column14”, type any}, {“Column15”, type any}, {“Column16”, type any}, {“Column17”, type any}, {“Column18”, type any}, {“Column19”, type any}, {“Column20″, type any}})
    in
    #”Changed Type”
    in GetResults

    What this does is take web URLs for several Excel files, downloads and parses them into one matrix. The website has sort of limiter and prevents me from doing more than 20 queries in a row.

    I also tried the function when calling it, e.g. “Function.InvokeAfter(GetData([example.com/sample.xlsx]), #duration(0,0,0,5))” but this doesn’t work either.

    How do I introduce the InvokeAfter function?

    1. Chris Webb – My name is Chris Webb, and I work on the Fabric CAT team at Microsoft. I blog about Power BI, Power Query, SQL Server Analysis Services, Azure Analysis Services and Excel.
      Chris Webb says:

      The first parameter of Function.InvokeAfter needs a value of type function, but when you say
      GetData([example.com/sample.xlsx])
      You are calling a function and returning a table. Try something like:
      Function.InvokeAfter(()=>GetData([example.com/sample.xlsx]), #duration(0,0,0,5))

  4. Hi Chris, please
    I need duration (0,0,0,5)
    for this

    #”Функция после сборки:

    let GetResults=(URL) =>
    let
    Источник = Web.Page(Web.Contents(URL)),
    Data0 = Источник{0}[Data],
    #”Пониженные заголовки” = Table.DemoteHeaders(Data0),
    #”Измененный тип” = Table.TransformColumnTypes(#”Пониженные заголовки”,{{“Column1”, type text}, {“Column2”, type text}, {“Column3”, type any}, {“Column4″, type text}}),
    Column3 = #”Измененный тип”{1}[Column3],
    #”Развернутый элемент Children” = Table.ExpandTableColumn(Column3, “Children”, {“Kind”, “Name”, “Children”, “Text”}, {“Kind.1”, “Name.1”, “Children.1”, “Text.1″}),
    #”Развернутый элемент Children.1″ = Table.ExpandTableColumn(#”Развернутый элемент Children”, “Children.1”, {“Kind”, “Name”, “Children”, “Text”}, {“Kind.2”, “Name.2”, “Children”, “Text.2″}),
    #”Развернутый элемент Children1″ = Table.ExpandTableColumn(#”Развернутый элемент Children.1”, “Children”, {“Kind”, “Name”, “Children”, “Text”}, {“Kind.3”, “Name.3”, “Children.1”, “Text.3″}),
    #”Развернутый элемент Children.2″ = Table.ExpandTableColumn(#”Развернутый элемент Children1”, “Children.1”, {“Kind”, “Name”, “Children”, “Text”}, {“Kind.4”, “Name.4”, “Children”, “Text.4″}),
    #”Развернутый элемент Children2″ = Table.ExpandTableColumn(#”Развернутый элемент Children.2”, “Children”, {“Kind”, “Name”, “Children”, “Text”}, {“Kind.5”, “Name.5”, “Children.1”, “Text.5″}),
    #”Строки с примененным фильтром” = Table.SelectRows(#”Развернутый элемент Children2″, each ([Text.5] null and [Text.5] “#(lf)” and [Text.5] “#(lf) ” and [Text.5] “#(lf) “)),
    #”Удаленные столбцы” = Table.RemoveColumns(#”Строки с примененным фильтром”,{“Text.4”, “Text.3”, “Text.2”, “Text.1”, “Text”, “Kind”, “Name”, “Kind.1”, “Name.1”, “Kind.2”, “Name.2”, “Kind.3”, “Name.3”, “Kind.4”, “Name.4”, “Kind.5”, “Name.5”, “Children.1″}),
    #”Транспонированная таблица” = Table.Transpose(#”Удаленные столбцы”),
    #”Удаленные столбцы1″ = Table.RemoveColumns(#”Транспонированная таблица”,{“Column1”, “Column4”, “Column5”, “Column6”, “Column7”, “Column8”, “Column9”, “Column10″})
    in
    #”Удаленные столбцы1″
    in GetResults

    and:

    let
    Источник = Excel.CurrentWorkbook(){[Name=”Ссылки”]}[Content],
    #”Измененный тип” = Table.TransformColumnTypes(Источник,{{“Об’єкт”, type text}, {“Текст ссилки”, type text}, {“URL”, type text}}),
    #”Добавлен пользовательский объект” = Table.AddColumn(#”Измененный тип”, “Пользовательская”, each #”Функция после сборки”([URL])),
    #”Развернутый элемент Пользовательская” = Table.ExpandTableColumn(#”Добавлен пользовательский объект”, “Пользовательская”, {“Column2”, “Column3”}, {“Column2”, “Column3″}),
    #”Измененный тип1″ = Table.TransformColumnTypes(#”Развернутый элемент Пользовательская”,{{“Column2”, type text}, {“Column3″, type text}})
    in
    #”Измененный тип1”

    thanks

  5. Hello Chris,

    I have the same problem. I can’t add function.invokeafter. Can you help me please?

    let
    Source = Json.Document(Web.Contents(“https://api.io/v1/catalog?X-API-KEY=” & “API-KEY” & “&limit=” & “25”)),
    pages = {1..Source[paging][totalpages]},
    #”Converted to Table” = Table.FromList(pages, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #”Changed Type” = Table.TransformColumnTypes(#”Converted to Table”,{{“Column1″, type text}}),
    #”Added Custom” = Table.AddColumn(#”Changed Type”, “Data”, each Json.Document(Web.Contents(“https://api.io/v1/catalog?X-API-KEY=” & “API-KEY” & “&limit=” & “25” & “&page=” & [Column1]))),
    #”Expanded Data” = Table.ExpandRecordColumn(#”Added Custom”, “Data”, {“items”}, {“items”}),
    #”Expanded items” = Table.ExpandListColumn(#”Expanded Data”, “items”),
    #”Expanded items1″ = Table.ExpandRecordColumn(#”Expanded items”, “items”, {“id”, “sku”, “status”, “highlight”, “new”, “stock”, “allow_extra_stock”, “showprice”, “weight”, “type”, “mode”, “views”, “permissions”, “id_brand”, “allow_cart_upload”, “unit_type”, “default_tax”, “default_imagem”, “thumb”, “prices”, “i18n”, “images”, “attachments”, “related_ids”, “extra_fields”, “specs”, “variations”}, {“items.id”, “items.sku”, “items.status”, “items.highlight”, “items.new”, “items.stock”, “items.allow_extra_stock”, “items.showprice”, “items.weight”, “items.type”, “items.mode”, “items.views”, “items.permissions”, “items.id_brand”, “items.allow_cart_upload”, “items.unit_type”, “items.default_tax”, “items.default_imagem”, “items.thumb”, “items.prices”, “items.i18n”, “items.images”, “items.attachments”, “items.related_ids”, “items.extra_fields”, “items.specs”, “items.variations”}),
    #”Expanded items.prices” = Table.ExpandListColumn(#”Expanded items1″, “items.prices”),
    #”Expanded items.prices1″ = Table.ExpandRecordColumn(#”Expanded items.prices”, “items.prices”, {“price_table”, “tax_included”, “master”, “allow_countries”, “type”, “values”}, {“items.prices.price_table”, “items.prices.tax_included”, “items.prices.master”, “items.prices.allow_countries”, “items.prices.type”, “items.prices.values”}),
    #”Expanded items.prices.values” = Table.ExpandListColumn(#”Expanded items.prices1″, “items.prices.values”),
    #”Expanded items.prices.values1″ = Table.ExpandRecordColumn(#”Expanded items.prices.values”, “items.prices.values”, {“normal_price”, “discount_price”, “start_discount_price”, “end_discount_price”}, {“items.prices.values.normal_price”, “items.prices.values.discount_price”, “items.prices.values.start_discount_price”, “items.prices.values.end_discount_price”}),
    #”Expanded items.i18n” = Table.ExpandRecordColumn(#”Expanded items.prices.values1″, “items.i18n”, {“pt”}, {“items.i18n.pt”}),
    #”Expanded items.i18n.pt” = Table.ExpandRecordColumn(#”Expanded items.i18n”, “items.i18n.pt”, {“title”, “description”, “small_description”, “specs”, “seo_url”, “seo_title”, “seo_description”, “seo_metatag”, “digital_file”, “barcode”}, {“items.i18n.pt.title”, “items.i18n.pt.description”, “items.i18n.pt.small_description”, “items.i18n.pt.specs”, “items.i18n.pt.seo_url”, “items.i18n.pt.seo_title”, “items.i18n.pt.seo_description”, “items.i18n.pt.seo_metatag”, “items.i18n.pt.digital_file”, “items.i18n.pt.barcode”}),
    #”Expanded items.images” = Table.ExpandListColumn(#”Expanded items.i18n.pt”, “items.images”),
    #”Expanded items.images1″ = Table.ExpandRecordColumn(#”Expanded items.images”, “items.images”, {“image”, “thumb”, “order”, “width”, “height”, “i18n”}, {“items.images.image”, “items.images.thumb”, “items.images.order”, “items.images.width”, “items.images.height”, “items.images.i18n”}),
    #”Expanded items.images.i18n” = Table.ExpandRecordColumn(#”Expanded items.images1″, “items.images.i18n”, {“pt”}, {“items.images.i18n.pt”}),
    #”Expanded items.attachments” = Table.ExpandListColumn(#”Expanded items.images.i18n”, “items.attachments”),
    #”Expanded items.attachments1″ = Table.ExpandRecordColumn(#”Expanded items.attachments”, “items.attachments”, {“file”, “order”, “i18n”}, {“items.attachments.file”, “items.attachments.order”, “items.attachments.i18n”}),
    #”Expanded items.attachments.i18n” = Table.ExpandRecordColumn(#”Expanded items.attachments1″, “items.attachments.i18n”, {“pt”}, {“items.attachments.i18n.pt”}),
    #”Expanded items.related_ids” = Table.ExpandListColumn(#”Expanded items.attachments.i18n”, “items.related_ids”),
    #”Expanded items.extra_fields” = Table.ExpandListColumn(#”Expanded items.related_ids”, “items.extra_fields”),
    #”Expanded items.extra_fields1″ = Table.ExpandRecordColumn(#”Expanded items.extra_fields”, “items.extra_fields”, {“id_extra_field”, “i18n”}, {“items.extra_fields.id_extra_field”, “items.extra_fields.i18n”}),
    #”Expanded items.extra_fields.i18n” = Table.ExpandRecordColumn(#”Expanded items.extra_fields1″, “items.extra_fields.i18n”, {“pt”}, {“items.extra_fields.i18n.pt”}),
    #”Expanded items.extra_fields.i18n.pt” = Table.ExpandRecordColumn(#”Expanded items.extra_fields.i18n”, “items.extra_fields.i18n.pt”, {“field”, “value”}, {“items.extra_fields.i18n.pt.field”, “items.extra_fields.i18n.pt.value”}),
    #”Expanded items.specs” = Table.ExpandListColumn(#”Expanded items.extra_fields.i18n.pt”, “items.specs”),
    #”Expanded items.variations” = Table.ExpandListColumn(#”Expanded items.specs”, “items.variations”)
    in
    #”Expanded items.variations”

  6. hi Chris,
    It seems not to be working for me. Can you help me add it to the code below (Query as text, ResultType as text) => if ResultType = “record” then
    let
    source = OData.Feed(“https://api.security.microsoft.com/api/” & Query, null, [Implementation=”2.0″, MoreColumns=true]),
    #”toList” = {source},
    #”toRecord” = Record.FromList(#”toList”, {“Record”})
    in
    #”toRecord”
    else
    let
    source = OData.Feed(“https://api.security.microsoft.com/api/” & Query, null, [Implementation=”2.0″, MoreColumns=true]),
    #”toTable” = #table(1, {{source}})
    in
    #”toTable”

  7. where is the error in this one ?

    (CNPJ as text) =>

    let
    Fonte = Function.InvokeAfter(()=>Json.Document(Web.Contents(“https://receitaws.com.br/v1/cnpj/&CNPJ”))),#duration(0,0,0,20))
    in
    Fonte

    1. Chris Webb – My name is Chris Webb, and I work on the Fabric CAT team at Microsoft. I blog about Power BI, Power Query, SQL Server Analysis Services, Azure Analysis Services and Excel.
      Chris Webb says:

      It looks like you have an extra bracket. Try:

      let
      Fonte = Function.InvokeAfter(()=>Json.Document(Web.Contents(“https://receitaws.com.br/v1/cnpj/&CNPJ”)),#duration(0,0,0,20))
      in
      Fonte

Leave a Reply to Chris WebbCancel reply