--[=[
	Executes all promises. If any fails, the result will be rejected. However, it yields until
	every promise is complete.

	:::warning
	Passing in a spare array (i.e. {nil, promise}) will result in undefined behavior here.
	:::

	@param promises { Promise<T> }
	@return Promise<T>
]=]

function PromiseUtils.all(promises)
	if #promises == 0 then
		return Promise.resolved()
	elseif #promises == 1 then
		return promises[1]
	end

	local remainingCount = #promises
	local returnPromise = Promise.new()
	local results = {}
	local allFulfilled = true

	local function syncronize(index, isFullfilled)
		return function(value)
			allFulfilled = allFulfilled and isFullfilled
			results[index] = value
			remainingCount = remainingCount - 1
			if remainingCount == 0 then
				local method = allFulfilled and "Resolve" or "Reject"
				returnPromise[method](returnPromise, unpack(results, 1, #promises))
			end
		end
	end

	for index, promise in pairs(promises) do
		promise:Then(syncronize(index, true), syncronize(index, false))
	end

	return returnPromise
end
