Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

Are functions used with table.sort allowed to yield?

Asked by
Xulp 0
8 years ago

Are functions used in table.sort not allowed to yield? It keeps erroring "attempt to yield across metamethod/C-call boundary". I think that is what that means but I'm not sure?

Here's the some of the code:

function Compare(F, S)
    local Settings = script.Parent.Parent.Parent:WaitForChild("Settings")
    local SortBy = Settings:WaitForChild("SortBy")
    if SortBy.Value == "rarity" then
        local Info1 = Market:GetProductInfo(F.AssetId.Value)
        local Info2 = Market:GetProductInfo(S.AssetId.Value)
        local S1 = Info1.Sales
        local S2 = Info2.Sales
        if S1 < S2 then
            return true
        elseif S2 < S1 then
            return false
        else
            if F.RAP.Value >= S.RAP.Value then
                return true
            else
                return false
            end         
        end
    else
        error("Invalid sort type")
    end
end

1 answer

Log in to vote
2
Answered by
Unclear 1776 Moderation Voter
8 years ago

Yielding

To yield means to pause the current thread for a certain amount of time. In RBX.Lua, the most common form of yielding is with the wait function. In your case, the method WaitForChild yields the current thread as well. Read the documentation on the method for more information.

As it turns out, when you call a function that yields, the lowest amount of time you can yield for is a single thread update. For example wait(0) still ends up yielding, just for the amount of time before the thread scheduler updates. The same thing with using WaitForChild on an object that already has a child that you are looking for applies. In your Compare function, you are definitely yielding.

Why does it matter

Callbacks in Lua are functions that are passed as arguments into functions. In programming, there are two types of callbacks: synchronous, which executes the callback immediately, and asynchronous, which executes the callback after a certain amount of yield time. For the sake of good design and to avoid conflicting with the language, you should always construct synchronous callbacks over asynchronous callbacks.

The type of callback requires depends on the case.

In this case, table.sort is expecting a synchronous callback. This is because table.sort is not a yield function; it will not pause your code until the table is sorted! If table.sort allowed for you to yield during sorting, then that would mean that there would be no guarantee that the rest of your code would be correct, assuming that the remainder of your code depends on the table to be sorted from table.sort.

Think of it like asking someone to do a task for you, and then immediately working on something else that entirely depends on the result of the task from before. It's impossible; you can't possibly know the results before they're calculated! The same thing applies here.

What you can do

In some cases, this can be pretty annoying. However, since table.sort is synchronous, we can assume that logic directly before it should be correct. We can move the scope of the variables that depend on WaitForChild outside of the callback.

Here is your original code...

function Compare(F, S)
    local Settings = script.Parent.Parent.Parent:WaitForChild("Settings")
    local SortBy = Settings:WaitForChild("SortBy")
    if SortBy.Value == "rarity" then
        local Info1 = Market:GetProductInfo(F.AssetId.Value)
        local Info2 = Market:GetProductInfo(S.AssetId.Value)
        local S1 = Info1.Sales
        local S2 = Info2.Sales
        if S1 < S2 then
            return true
        elseif S2 < S1 then
            return false
        else
            if F.RAP.Value >= S.RAP.Value then
                return true
            else
                return false
            end         
        end
    else
        error("Invalid sort type")
    end
end
-- ... some code here
table.sort(t, Compare) -- assuming some table t

And here is your code with some rearranging...

local Settings = script.Parent.Parent.Parent:WaitForChild("Settings")
local SortBy = Settings:WaitForChild("SortBy")

table.sort(t, function(F, S)
    if SortBy.Value == "rarity" then
        local Info1 = Market:GetProductInfo(F.AssetId.Value)
        local Info2 = Market:GetProductInfo(S.AssetId.Value)
        local S1 = Info1.Sales
        local S2 = Info2.Sales
        if S1 < S2 then
            return true
        elseif S2 < S1 then
            return false
        else
            if F.RAP.Value >= S.RAP.Value then
                return true
            else
                return false
            end         
        end
    else
        error("Invalid sort type")
    end
end)

Notice how all we did was move the yielding outside of the callback to a location where the callback could still access it. There's no reason to continuously check if an object exists during a synchronous callback because you can depend on Lua to execute the entire callback during a single update! If the object exists during the first part of the code, then you are pretty much guaranteed that it will exist for the remainder of the update.

Ad

Answer this question