Em 2015, lançamos a sincronização em segundo plano, que permite que o service worker adie o trabalho até que o usuário tenha conectividade. Isso significa que o usuário pode digitar uma mensagem, clicar em "Enviar" e sair do site sabendo que a mensagem será enviada agora ou quando ele tiver conectividade.
É um recurso útil, mas exige que o service worker fique ativo durante toda a busca. Isso não é um problema para tarefas curtas, como enviar uma mensagem, mas se a tarefa demorar muito, o navegador vai encerrar o service worker. Caso contrário, isso representa um risco para a privacidade e a bateria do usuário.
E se você precisar baixar algo que pode levar muito tempo, como um filme, podcasts ou níveis de um jogo? É para isso que serve a busca em segundo plano.
A busca em segundo plano está disponível por padrão desde o Chrome 74.
Confira uma demonstração rápida de dois minutos mostrando o estado tradicional das coisas em comparação com o uso da busca em segundo plano:
Como funciona
Uma busca em segundo plano funciona assim:
- Você diz ao navegador para realizar um grupo de buscas em segundo plano.
- O navegador busca esses elementos, mostrando o progresso para o usuário.
- Quando a busca é concluída ou falha, o navegador abre o service worker e dispara um evento para informar o que aconteceu. É aqui que você decide o que fazer com as respostas, se houver alguma coisa.
Se o usuário fechar as páginas do seu site após a etapa 1, não tem problema, o download vai continuar. Como a busca é altamente visível e pode ser interrompida com facilidade, não há o problema de privacidade de uma tarefa de sincronização em segundo plano muito longa. Como o service worker não está em execução constante, não há preocupação de que ele possa abusar do sistema, como minerar bitcoin em segundo plano.
Em algumas plataformas, como o Android, é possível que o navegador seja fechado após a etapa 1, já que ele pode transferir a busca para o sistema operacional.
Se o usuário iniciar o download off-line ou ficar off-line durante o processo, a busca em segundo plano será pausada e retomada depois.
A API
Detecção de recursos
Como em qualquer novo recurso, é preciso detectar se o navegador é compatível com ele. Para a busca em segundo plano, é simples assim:
if ('BackgroundFetchManager' in self) {
// This browser supports Background Fetch!
}
Como iniciar uma busca em segundo plano
A API principal depende de um registro de service worker. Portanto, verifique se você já registrou um. Em seguida:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch('my-fetch', ['/ep-5.mp3', 'ep-5-artwork.jpg'], {
title: 'Episode 5: Interesting things.',
icons: [{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
}],
downloadTotal: 60 * 1024 * 1024,
});
});
backgroundFetch.fetch
usa três argumentos:
Parâmetros | |
---|---|
id |
string identifica exclusivamente essa busca em segundo plano.
|
requests |
Array<Request|string>
Os itens a serem buscados. As strings serão tratadas como URLs e transformadas em Request s via new Request(theString) .
É possível buscar itens de outras origens, desde que os recursos permitam isso via CORS. Observação:no momento, o Chrome não é compatível com solicitações que exigem um pré-lançamento do CORS. |
options |
Um objeto que pode incluir o seguinte: |
options.title |
string Um título para o navegador mostrar junto com o progresso. |
options.icons |
Array<IconDefinition> Uma matriz de objetos com "src", "size" e "type". |
options.downloadTotal |
number O tamanho total dos corpos de resposta (após serem descompactados com gzip). Embora seja opcional, recomendamos que você forneça essa informação. Usado para informar ao usuário o tamanho do download e fornecer informações de progresso. Se você não fornecer isso, o navegador vai informar ao usuário que o tamanho é desconhecido, e, como resultado, ele poderá interromper o download. Se o número de downloads da busca em segundo plano exceder o número informado aqui, ela será cancelada. Não há problema se o download for menor que |
backgroundFetch.fetch
retorna uma promessa que é resolvida com um BackgroundFetchRegistration
. Vou
explicar os detalhes disso mais tarde. A promessa será rejeitada se o usuário tiver desativado os downloads ou se um dos parâmetros fornecidos for inválido.
Ao fornecer muitas solicitações para uma única busca em segundo plano, você pode combinar coisas que são logicamente uma única coisa para o usuário. Por exemplo, um filme pode ser dividido em milhares de recursos (típico com MPEG-DASH) e vir com recursos extras, como imagens. Um nível de um jogo pode ser distribuído por muitos recursos de JavaScript, imagem e áudio. Mas para o usuário, é apenas "o filme" ou "o nível".
Como conseguir uma busca em segundo plano
É possível receber uma busca em segundo plano assim:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});
…transmitindo o ID da busca em segundo plano que você quer. get
retorna undefined
se não houver uma busca em segundo plano ativa com esse ID.
Uma busca em segundo plano é considerada "ativa" desde o momento em que é registrada até que seja concluída, falhe ou seja interrompida.
É possível conferir uma lista de todas as buscas em segundo plano ativas usando getIds
:
navigator.serviceWorker.ready.then(async (swReg) => {
const ids = await swReg.backgroundFetch.getIds();
});
Registros de busca em segundo plano
Um BackgroundFetchRegistration
(bgFetch
nos exemplos acima) tem o seguinte:
Propriedades | |
---|---|
id |
string O ID da busca em segundo plano. |
uploadTotal |
number O número de bytes a serem enviados ao servidor. |
uploaded |
number O número de bytes enviados. |
downloadTotal |
number O valor fornecido quando a busca em segundo plano foi registrada ou zero. |
downloaded |
number O número de bytes recebidos com sucesso. Esse valor pode diminuir. Por exemplo, se a conexão cair e o download não puder ser retomado, o navegador vai reiniciar a busca desse recurso do zero. |
result |
Opções:
|
failureReason |
Opções:
|
recordsAvailable |
boolean É possível acessar as solicitações/respostas subjacentes? Quando isso é falso, |
Métodos | |
abort() |
Retorna Promise<boolean> . Aborta a busca em segundo plano. A promessa retornada é resolvida com "true" se a busca for cancelada. |
matchAll(request, opts) |
Retorna Promise<Array<BackgroundFetchRecord>> Receba as solicitações e respostas. Os argumentos aqui são os mesmos da API de cache. Chamar sem argumentos retorna uma promessa para todos os registros. Veja mais detalhes abaixo. |
match(request, opts) |
Retorna Promise<BackgroundFetchRecord> . Como acima, mas resolve com a primeira correspondência. |
Eventos | |
progress |
Acionada quando qualquer um dos valores uploaded , downloaded , result ou failureReason muda. |
Acompanhamento do progresso
Isso pode ser feito usando o evento progress
. Lembre-se de que downloadTotal
é qualquer valor que você forneceu ou 0
se você não forneceu um valor.
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(bgFetch.downloaded / bgFetch.downloadTotal * 100);
console.log(`Download progress: ${percent}%`);
});
Como receber solicitações e respostas
bgFetch.match('/ep-5.mp3').then(async (record) => {
if (!record) {
console.log('No record found');
return;
}
console.log(`Here's the request`, record.request);
const response = await record.responseReady;
console.log(`And here's the response`, response);
});
record
é um BackgroundFetchRecord
e tem esta aparência:
Propriedades | |
---|---|
request |
Request A solicitação fornecida. |
responseReady |
Promise<Response> A resposta buscada. A resposta está atrás de uma promessa porque talvez ainda não tenha sido recebida. A promessa será rejeitada se a busca falhar. |
Eventos de service worker
Eventos | |
---|---|
backgroundfetchsuccess |
Tudo foi buscado com sucesso. |
backgroundfetchfailure |
Uma ou mais buscas falharam. |
backgroundfetchabort |
Uma ou mais buscas falharam.
Isso só é útil se você quiser limpar dados relacionados. |
backgroundfetchclick |
O usuário clicou na interface de progresso do download. |
Os objetos de evento têm o seguinte:
Propriedades | |
---|---|
registration |
BackgroundFetchRegistration |
Métodos | |
updateUI({ title, icons }) |
Permite mudar o título/ícones definidos inicialmente. Isso é opcional, mas permite que você
forneça mais contexto, se necessário. Você só pode fazer isso *uma vez* durante
eventos backgroundfetchsuccess e backgroundfetchfailure . |
Reagir a sucesso/falha
Já vimos o evento progress
, mas ele só é útil enquanto o usuário tem uma página aberta no seu site. O principal benefício da busca em segundo plano é que as coisas continuam funcionando depois que o usuário sai da
página ou até mesmo fecha o navegador.
Se a busca em segundo plano for concluída, o service worker vai receber o evento
backgroundfetchsuccess
, e event.registration
será o registro da busca em segundo plano.
Depois desse evento, as solicitações e respostas buscadas não ficam mais acessíveis. Se quiser mantê-las, mova-as para algum lugar, como a API Cache.
Como na maioria dos eventos do service worker, use event.waitUntil
para que o service worker saiba quando o evento
estiver concluído.
Por exemplo, no service worker:
addEventListener('backgroundfetchsuccess', (event) => {
const bgFetch = event.registration;
event.waitUntil(async function() {
// Create/open a cache.
const cache = await caches.open('downloads');
// Get all the records.
const records = await bgFetch.matchAll();
// Copy each request/response across.
const promises = records.map(async (record) => {
const response = await record.responseReady;
await cache.put(record.request, response);
});
// Wait for the copying to complete.
await Promise.all(promises);
// Update the progress notification.
event.updateUI({ title: 'Episode 5 ready to listen!' });
}());
});
A falha pode ter sido causada por um único 404, que talvez não fosse importante para você. Portanto, ainda vale a pena copiar algumas respostas para um cache, como acima.
Reagir ao clique
A interface que mostra o progresso e o resultado do download é clicável. O evento backgroundfetchclick
no
service worker permite que você reaja a isso. Como acima, event.registration
será o registro de busca
em segundo plano.
O mais comum é abrir uma janela com esse evento:
addEventListener('backgroundfetchclick', (event) => {
const bgFetch = event.registration;
if (bgFetch.result === 'success') {
clients.openWindow('/latest-podcasts');
} else {
clients.openWindow('/download-progress');
}
});
Outros recursos
Correção: uma versão anterior deste artigo se referiu incorretamente ao Background Fetch como um "padrão da Web". No momento, a API não está na faixa de padrões. A especificação pode ser encontrada no WICG como um relatório preliminar do grupo da comunidade.