How to keep the order of floats in Plain TeX?

You can use \write primitive and read the right order of figures from auxiliary file in second run of TeX:

\newcount\figno
\newwrite\fileout
\newread\filein

\def\pgset#1{\advance\figno by1
   \expandafter\edef\csname pg:#1\endcsname{\the\figno}}

\openin\filein=\jobname.pg
\ifeof\filein \else \closein\filein \input\jobname.pg \fi
\figno=0

\immediate\openout\fileout=\jobname.pg
\def\printfigno{\global\advance\figno by1
   \expandafter\ifx\csname pg:\the\figno\endcsname\relax
   ??\the\figno \else \csname pg:\the\figno\endcsname \fi
   \edef\tmp{\write\fileout{\string\pgset{\the\figno}}}\tmp
}
\def\bsptext{%
Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert. Das ist ein Test. Das ist ein guter Test. Das ist ein langer Test. Mal sehen, ob alles funktioniert.}

\def\makefigno{Fig. \printfigno}
\def\mypicture{\vrule height 88pt depth 20pt width300pt}

\bsptext
\midinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\topinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\midinsert
\mypicture \makefigno
\endinsert

\bsptext

\bye

Update: A major rework after understanding the question better (see comments above).

Plain TeX keeps the right sequence for each insertion class as stated on page 122 of the TeXbook:

Each class of insertions is independent, but \TeX preserves the order of insertions within a class.

There is only one insertion class for floats: \topinsert. All pageinserts are topinserts and midinserts are implemented using either topinsert or they use direct output. Thus topinsert and pageinserts are always in the right order but midinserts might get out of sequence.

For example, in your example Figure 1 (midinsert) is inserted after the first bsptext. Then you request a topinsert, i.e., a figure that has to be placed at the top of the current page or if that is not possible at the top of the next page. Here it is possible so it starts the current page. The next topinsert is placed after the first because it fits too. This mix gives inserts out of order.

The TeXbook describes the general procedure in chapter 15 on pages 115--116 (dangerous bend paragraphs); look at the example above exercise 15.5 and at this exercise for an example with topinserts and a single midinsert. The general procedure how inserts are added to pages is given on page 123 (double dangerous bend paragraphs).

On page 363 of the TeXbook you find the code. Look at the definition of \endinsert to see the conversion of midinserts: The first \if@mid checks if the insert fits on the page; if not @midfalse is set and the second \if@mid outputs a topinsert.

Conclusions:

(1) Midinserts and topinserts cannot be used in parallel without the risk of an out-of-order sequence;

(2) using only topinserts and pageinserts guarantees to keep the order.

(3) A new insertion class could be defined that places the floats not on the top of pages if (2) is not liked. In this class the order is kept. But this means also a change to the output routine as it must handle the insertions.

(4) A new command similar to midinserts can be defined that outputs floats either directly or converts them to topinserts and forces after the first conversion for the rest of the page additional midinserts to topinserts and topinserts to midinserts. This does not require a change to the output routine.

An approach for (3) can be found in this TUGboat article by David Salomon: http://www.tug.org/TUGboat/tb11-4/tb30salomon.pdf; see page 593ff.

How to implement (4)? A couple of \ifs are set if a new midinsert-like command is processed, let's call it seqinsert. If it is placed in the text \seqinsertplacedtrue is executed which will convert topinserts for the page. If a seqinsert is converted to a topinsert \blockseqinserts is set and all following seqinserts on the page are converted to topinserts too. Both flags are reset when the page number is increased; this happens in the macro \advancepageno.

\newif\ifitisaseqinsert  % true: a seqinsert is processed (used in \endinsert)
\newif\ifseqinsertplaced % true: a seqinsert was output as midinsert
\newif\ifblockseqinserts % true: a seqinsert was moved to the next page as topinsert

\def\seqinsert{\itisaseqinserttrue\midinsert}% extended \midinsert (not a \newinsert !)
\catcode`@=11
%               harmless changes to \endinsert; no problem with existing plain
\def\endinsert{\egroup % finish the \vbox
  \ifseqinsertplaced \if@mid\else \@midtrue\fi\fi                    % new (A)
  \if@mid
    \ifitisaseqinsert \ifblockseqinserts \@midfalse\p@gefalse\fi\fi  % new (B)
    \dimen@\ht\z@ \advance\dimen@\dp\z@ \advance\dimen@12\p@
    \advance\dimen@\pagetotal \advance\dimen@-\pageshrink
    \ifdim\dimen@>\pagegoal\@midfalse\p@gefalse
       \ifitisaseqinsert \global\blockseqinsertstrue                 % new (C)
          \global\seqinsertplacedfalse\fi                            % new
    \fi\fi
  \if@mid \bigskip\box\z@\bigbreak
     \ifitisaseqinsert \global\seqinsertplacedtrue\fi                % new (D)
  \else\insert\topins{\penalty100 % floating insertion
    \splittopskip\z@skip
    \splitmaxdepth\maxdimen \floatingpenalty\z@
    \ifp@ge \dimen@\dp\z@
    \vbox to\vsize{\unvbox\z@\kern-\dimen@}% depth is zero
    \else \box\z@\nobreak\bigskip\fi}\fi\endgroup
  \itisaseqinsertfalse}                                              % new (E)
\catcode`@=12
% minor addition for the output routine
\let\orgadvancepageno=\advancepageno
\def\advancepageno{\orgadvancepageno
   \global\blockseqinsertsfalse\global\seqinsertplacedfalse}% reset flags for every page

Here are the explanations of the changes to \endinsert:

(A) converts topinserts to midinserts if one seqinsert was not converted to a topinsert

(B) a seqinsert was converted to a topinsert so the next is converted too

(C) here the flag for the conversion seqinsert to topinsert is set (and the flag for (D) is reset to avoid overhead by (A))

(D) here the flag for a non-converted seqinsert is set

(E) reset the flag that shows that seqinsert was called

If this code is added to the example topinserts, pageinserts, and seqinserts can be used without getting out of sequence; the command \midinsert might destroy the sequence and should not be used.

For example, add the above code and change your output to:

\bsptext
\topinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\topinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\topinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\seqinsert \mypicture \makefigno \endinsert
\bsptext

Note, I never used this code; I created it to answer the question.