You can just be sneaky about it and have the system use "offset" for you:
@RemoteAction public static SObject[] getRecords(Id offsetId) {
if(offsetId==null) {
return [SELECT Id,Name FROM Contact ORDER BY Id ASC LIMIT 100];
} else {
return [SELECT Id,Name FROM Contact WHERE ID > :offsetId ORDER BY Id ASC LIMIT 100];
}
}
From here, you can then just query away happily:
function handleResult(result, event) {
// handle a batch here
// do more
if(result.length===100)
{!$RemoteAction.controller.getRecords}(result[99].Id,handleResult);
}
}
{!$RemoteAction.controller.getRecords}(null,handleResult);
This works even if you combine it with other criteria; you don't have to filter on Id alone. We're just using the ID to leverage the index and create our own pseudo-offset.