\next is not a predefined operator. It's just a scratch control sequence that gets redefined several times during the loop.
What happens when \bar is called (by the way it's a bad name, because \bar is a predefined control sequence that's used for a math accent)?
Its definition has parameter text #1 #2, which means that TeX will absorb everything until the first space token, assigning it to the parameter #1 and then a further token or braced list of tokens, assigning it to the parameter #2.
Next a long string of \expandafter tokens is executed. Let's say you have the input \bar 2 3 4 \relax.
In this case #1 is 2 and #2 is 3. Then TeX will do
\expandafter\def\expandafter\foo\expandafter{\foo {}2}
Each \expandafter tokens is touched in turns and the last one causes \foo to be expanded to its current meaning, so you get
\def\foo{1{}2}
Such definition is stored and then the next line is looked at, so \next is assigned the same meaning as \bar. Then #2 is compared to \relax; in this case the test returns false, because #2 is 3, so the text up to \fi is skipped over. It remains
\next#2
so now TeX is confronted with
\bar3 4 \relax
The same thing now happens twice, so we arrive at
\def\foo{1{}2{}3{}4}
but now there's a difference, because #2 now is \relax. So the \ifx test returns true and TeX does \let\next\relax, so the last instruction becomes
\relax\relax
and the loop ends.
Not a particularly good way to do the task, but it works. By the way, a % is missing at the end of the \expandafter line and there are redundant ones in the next two lines.
If you want to stop the loop earlier you need to modify the macros and introduce a counter to index the number of runs.
Maybe a more modern loop is what you want.
\documentclass{article}
\ExplSyntaxOn
\seq_new:N \l__aram_addto_seq
\NewDocumentCommand{\aramaddto}{mom}
{
% first split the final argument at spaces (here ~ stands for a space)
\seq_set_split:Nnn \l__aram_addto_seq { ~ } { #3 }
\IfNoValueTF { #2 }
{% no optional argument, add everything
\tl_set:Nx #1 { \exp_not:V #1 \seq_use:Nn \l__aram_addto_seq { } }
}
{% optional argument, only add as many items as required
\seq_map_indexed_inline:Nn \l__aram_addto_seq
{% ##1 is the current index, ##2 is the item
\int_compare:nTF { ##1 < #2 }
{% still wanting to add
\tl_put_right:Nn #1 { ##2 }
}
{% flush
\seq_map_break:
}
}
}
}
\ExplSyntaxOff
\begin{document}
\newcommand{\fooA}{1}
\aramaddto\fooA{2 3 4}
Should print 1234: \fooA
\newcommand{\fooB}{1}
\aramaddto\fooB[4]{2 3 4 5 6}
Should print 1234: \fooB
\end{document}

After the comments, here's a way how to accomplish the aim, that is, input a list of numbers and multiplying them all, with the option to stop when one of the numbers is greater than a stated upper bound.
\documentclass{article}
\usepackage{xfp}
\ExplSyntaxOn
\NewDocumentCommand{\genfactorial}{om}
{
\IfNoValueTF { #1 }
{
\aram_genfactorial:en { \clist_item:nn { #2 } { -1 } } { #2 }
}
{
\aram_genfactorial:nn { #1 } { #2 }
}
}
\seq_new:N \l__aram_genfactorial_seq
\cs_new_protected:Nn \aram_genfactorial:nn
{
\seq_clear:N \l__aram_genfactorial_seq
\clist_map_inline:nn { #2 }
{
\int_compare:nTF { ##1 <= #1 }
{ \seq_put_right:Nn \l__aram_genfactorial_seq { ##1 } }
{ \clist_map_break: }
}
\fp_eval:n { \seq_use:Nn \l__aram_genfactorial_seq { * } }
}
\cs_generate_variant:Nn \aram_genfactorial:nn { e }
\ExplSyntaxOff
\begin{document}
\genfactorial{1,2,3,4}
\genfactorial[45]{1,2,3,4,5,6,45,67,89}
\end{document}
Using a comma separated list seems better than separating items with spaces.

Update
With expl3 released 2021-05-07, we can make the above fully expandable, due to the new \clist_map_tokens:nn function.
The check is made with 0 as default optional argument, assuming that the input will consist of positive numbers.
The given input is mapped item by item and *<item> is appended, but the <item> is transformed into 1 if the optional argument is 0 or the upper bound is exceeded.
\documentclass{article}
\usepackage{xfp}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\genfactorial}{O{0}m}
{
\aram_genfactorial:nn { #1 } { #2 }
}
\cs_new:Nn \aram_genfactorial:nn
{
\fp_eval:n
{
1
\clist_map_tokens:nn { #2 } { __aram_genfactorial:nn { #1 } }
}
}
\cs_new:Nn __aram_genfactorial:nn
{
\int_compare:nTF { #1 == 0 }
{% no bound
* #2
}
{% #1 is the upper bound
\int_compare:nTF { #2 <= #1 } { * #2 } { * 1 }
}
}
\ExplSyntaxOff
\begin{document}
\genfactorial{1,2,3,4}
\genfactorial[4]{1,2,3,4,5,6,45,67,89}
\genfactorial[7]{1,2,3,4,5,6,45,67,89}
\genfactorial[45]{1,2,3,4,5,6,45,67,89}
\edef\test{\genfactorial{1,2,3,4,5,6,45,67,89}}\test
\end{document}
The last line shows that the function is indeed fully expandable.
