You should wrap each batch in a transaction.
I would not reselect the items to delete. You should get the items to delete from the target table. Reselecting them to delete creates the opportunity to remove items not inserted.
SET @results = 50000
WHILE (@results = 50000) -- This prevents the loop from running 1 extra time
BEGIN
BEGIN TRAN
DROP TABLE IF EXISTS #pklist;
SELECT TOP (50000)
<primary key>
INTO #pklist
from source_table with (nolock)
where date <= '2021-12-31 23:59:59'
order by <primary key> asc
SET @results = @@ROWCOUNT;
IF @results > 0
BEGIN
insert into bkp_tables
(col1,col2,col3)
select col1,col2,col3
from source_table with (nolock)
INNER JOIN #pklist ON pk = pk
delete from source_table
INNER JOIN #pklist ON pk = pk
END
COMMIT
END