Подтвердить что ты не робот

Возвращение значения API хранения Chrome без функции

За последние два дня я работал с хром-асинхронным хранилищем. Он работает "отлично", если у вас есть функция. (Как ниже):

chrome.storage.sync.get({"disableautoplay": true}, function(e){
console.log(e.disableautoplay);
});

Моя проблема в том, что я не могу использовать функцию с тем, что я делаю. Я хочу просто вернуть его, например, LocalStorage. Что-то вроде:

var a = chrome.storage.sync.get({"disableautoplay": true});

или

var a = chrome.storage.sync.get({"disableautoplay": true}, function(e){
    return e.disableautoplay;
});

Я пробовал миллион комбинаций, даже устанавливая общедоступную переменную и устанавливая, что:

var a;
window.onload = function(){
    chrome.storage.sync.get({"disableautoplay": true}, function(e){
    a = e.disableautoplay;
});
}

Ничего не работает. Все возвращается undefined, если код, ссылающийся на него, не находится внутри функции get, и это бесполезно для меня. Я просто хочу иметь возможность возвращать значение как переменную.

Возможно ли это?

EDIT: этот вопрос не является дубликатом, пожалуйста, позвольте мне объяснить, почему:

1: Нет других сообщений, спрашивающих об этом конкретно (я провел два дня, глядя сначала, на всякий случай).

2: Мой вопрос все еще не ответил. Да, Chrome Storage является асинхронным, и да, он не возвращает значение. Это проблема. Ниже я расскажу...

Мне нужно получить хранимое значение вне функции chrome.storage.sync.get. я -cannot-use localStorage, так как он специфичен для URL-адреса, и одни и те же значения не могут быть доступны со страницы browser_action хром-расширения и background.js. Я не могу сохранить значение с помощью одного script и получить доступ к нему другим. Они рассматриваются отдельно.

Итак, мое единственное решение - использовать Chrome Storage. Должен быть какой-то способ получить значение сохраненного элемента и ссылаться на него вне функции get. Мне нужно проверить его в выражении if.

Точно так же, как localStorage может выполнять

if(localStorage.getItem("disableautoplay") == true);

Должен быть какой-то способ сделать что-то в строках

if(chrome.storage.sync.get("disableautoplay") == true);

Я понимаю, что это будет не так просто, но что лучший способ я могу объяснить.

Каждое сообщение, которое я вижу, говорит, чтобы сделать это следующим образом:

chrome.storage.sync.get({"disableautoplay": true, function(i){
    console.log(i.disableautoplay);
    //But the info is worthless to me inside this function.
});
//I need it outside this function.
4b9b3361

Ответ 1

Хорошо, хорошо, вы получите свой индивидуальный ответ на свой вопрос. Это все еще будет 90% -ным объяснением, почему вы не можете обойти асинхронные, но потерпите меня - это поможет вам в целом. Я обещаю, что в конце концов есть что-то подходящее к chrome.storage.

Прежде чем мы начнем, я повторю канонические ссылки для этого:

Итак, давайте обсудим асинхронность JS.

Раздел 1: Что это?

Первая концепция, которую нужно охватить - это среда выполнения. JavaScript, в некотором роде, встроен в другую программу, которая контролирует поток выполнения - в данном случае, Chrome. Все происходящие события (таймеры, щелчки и т.д.) Происходят из среды выполнения. Код JavaScript регистрирует обработчики событий, которые запоминаются средой выполнения и вызываются соответствующим образом.

Во-вторых, важно понимать, что JavaScript является однопоточным. Существует один цикл обработки событий, поддерживаемый средой выполнения; если есть какой-то другой код, выполняемый, когда происходит событие, это событие помещается в очередь для обработки , когда текущий код завершается.

Посмотрите на этот код:

var clicks = 0;

someCode();
element.addEventListener("click", function(e) {
  console.log("Oh hey, I'm clicked!");
  clicks += 1;
});
someMoreCode();

Итак, что здесь происходит? Когда этот код выполняется, когда выполнение достигает .addEventListener, происходит следующее: среда выполнения уведомляется о том, что когда происходит событие (нажатие element), оно должно вызвать функцию-обработчик.

Важно понимать (хотя в данном конкретном случае это довольно очевидно), что функция не запускается в этот момент. Он будет запущен только позже, когда это событие произойдет. Выполнение продолжается, как только среда выполнения подтверждает, что "когда я это сделаю", я выполню (или "перезвоню", отсюда и название "обратный вызов"). Если someMoreCode() попытается получить доступ к clicks, это будет 0, а не 1.

Это то, что называется асинхронностью, поскольку это происходит вне текущего потока выполнения.

Раздел 2: Зачем это нужно или почему вымирают синхронные API?

Теперь важное соображение. Предположим, что someMoreCode() на самом деле очень долго работающий кусок кода. Что произойдет, если событие щелчка произошло во время его работы?

В JavaScript нет концепции прерываний. Среда выполнения увидит, что выполняется код, и поместит вызов обработчика событий в очередь. Обработчик не будет выполнен до полного завершения someMoreCode().

Хотя обработчик события щелчка является экстремальным в том смысле, что щелчок не гарантированно произойдет, это объясняет , почему вы не можете ждать результата асинхронной операции. Вот пример, который не будет работать:

element.addEventListener("click", function(e) {
  console.log("Oh hey, I'm clicked!");
  clicks += 1;
});
while(1) {
  if(clicks > 0) {
    console.log("Oh, hey, we clicked indeed!");
    break;
  }
}

Вы можете щелкнуть по своему сердечному содержанию, но код, который увеличит clicks, терпеливо ожидает завершения (не прекращающегося) цикла. К сожалению.

Обратите внимание, что этот фрагмент кода не только замораживает этот фрагмент кода: каждое отдельное событие больше не обрабатывается, пока мы ожидаем, поскольку существует только одна очередь/поток событий. В JavaScript есть только один способ позволить другим обработчикам выполнять свою работу: завершать текущий код и сообщать среде выполнения, что вызывать, когда происходит что-то, что нам нужно.


Вот почему асинхронная обработка применяется к другому классу вызовов:

  • требует выполнения, а не JS, чтобы что-то делать (например, доступ к диску/сети)
  • гарантированно прекращается (в случае успеха или неудачи)

Отпусти с классическим примером: AJAX звонки. Предположим, мы хотим загрузить файл с URL.

  • Допустим, что при нашем текущем соединении среда выполнения может запрашивать, загружать и обрабатывать файл в форме, которую можно использовать в JS за 100 мс.
  • При другом подключении, что еще хуже, это займет 500 мс.
  • И иногда соединение действительно плохое, поэтому среда выполнения будет ждать 1000 мс и сдается по истечении времени ожидания.

Если бы мы ожидали, пока это не завершится, у нас была бы переменная, непредсказуемая и относительно длительная задержка. Из-за того, как работает ожидание JS, все другие обработчики (например, пользовательский интерфейс) не будут выполнять свою работу в течение этой задержки, что приведет к остановке страницы.

Звучит знакомо? Да, именно так работает синхронный XMLHttpRequest. Вместо цикла while(1) в коде JS это по существу происходит в коде времени выполнения - поскольку JavaScript не может позволить другому коду выполняться во время ожидания.

Да, это допускает знакомую форму кода:

var file = get("http://example.com/cat_video.mp4");

Но ужасной, ужасной ценой замерзания. Стоимость настолько ужасна, что современные браузеры считают ее устаревшей. Здесь обсуждается тема MDN.


Теперь давайте посмотрим на localStorage. Он соответствует описанию "завершение вызова во время выполнения", и все же он является синхронным. Почему?

Проще говоря: исторические причины (это очень старая спецификация).

Хотя это, безусловно, более предсказуемо, чем сетевой запрос, localStorage все еще нуждается в следующей цепочке:

JS code <-> Runtime <-> Storage DB <-> Cache <-> File storage on disk

Это сложная цепочка событий, и весь движок JS должен быть остановлен для этого. Это приводит к , что считается недопустимым исполнением.


Теперь API Chrome изначально созданы для повышения производительности. Вы все еще можете видеть некоторые синхронные вызовы в более старых API, таких как chrome.extension, и есть вызовы, которые обрабатываются в JS (и поэтому имеют смысл как синхронные), но chrome.storage (относительно) является новым.

Таким образом, он охватывает парадигму "Я подтверждаю ваш звонок и вернусь с результатами, теперь сделайте что-то полезное между тем", если есть задержка, связанная с выполнением чего-либо во время выполнения. Синхронных версий этих вызовов нет, в отличие от XMLHttpRequest.

Цитирование документов:

Он [chrome.storage] асинхронен с массовыми операциями чтения и записи, и поэтому быстрее, чем блокирующий и последовательный API localStorage.

Раздел 3: Как принять асинхронность?

Классическим способом борьбы с асинхронностью являются цепочки обратных вызовов.

Предположим, у вас есть следующий синхронный код:

var result = doSomething();
doSomethingElse(result);

Предположим, что теперь doSomething является асинхронным. Тогда это становится:

doSomething(function(result) {
  doSomethingElse(result);
});

Но что, если это еще сложнее? Скажи, что это было:

function doABunchOfThings() {
  var intermediate = doSomething();
  return doSomethingElse(intermediate);
}

if (doABunchOfThings() == 42) {
  andNowForSomethingCompletelyDifferent()
}

Хорошо.. В этом случае вам нужно переместить все это в обратный вызов. Вместо этого return должен стать вызовом.

function doABunchOfThings(callback) {
  doSomething(function(intermediate) {
    callback(doSomethingElse(intermediate));
  });
}

doABunchOfThings(function(result) {
  if (result == 42) {
    andNowForSomethingCompletelyDifferent();
  }
});

Здесь у вас есть цепочка обратных вызовов: doABunchOfThings немедленно вызывает doSomething, что завершается, но через некоторое время вызывает doSomethingElse, результат которого передается в if через другой обратный вызов.

Очевидно, что это может привести к беспорядку. Ну, никто не сказал, что JavaScript - хороший язык. Добро пожаловать в Callback Hell.

Есть инструменты, чтобы сделать его более управляемым, например Promises. Я не буду обсуждать их здесь (не хватает места), но они не меняют основную часть "этот код будет выполняться только позже".

Section TL;DR: я absolutely must have the storage synchronous, halp!

Секция TL; DR: мне абсолютно необходимо, чтобы хранилище было синхронным, хелп! Иногда есть законные причины иметь синхронное хранилище. Например, вызовы блокировки API webRequest не могут ждать. Или Callback Hell дорого обойдется вам.

То, что вы можете сделать, это иметь синхронный кеш асинхронного chrome.storage. Это связано с некоторыми затратами, но это не невозможно.

Рассмотрим:

var storageCache = {};
chrome.storage.sync.get(null, function(data) {
  storageCache = data;
  // Now you have a synchronous snapshot!
});
// Not HERE, though, not until "inner" code runs

Если вы можете поместить ВСЕ свой код инициализации в одну функцию init(), то у вас есть это:

var storageCache = {};
chrome.storage.sync.get(null, function(data) {
  storageCache = data;
  init(); // All your code is contained here, or executes later that this
});

К тому времени, когда исполняется код в init(), и после этого , когда происходит любое событие, которому были назначены обработчики в init(), storageCache будет заполняться. Вы уменьшили асинхронность до ОДНОГО обратного вызова.

Конечно, это всего лишь снимок того, что хранилище смотрит во время выполнения get(). Если вы хотите сохранить согласованность с хранилищем, вам необходимо настроить обновления для storageCache с помощью событий chrome.storage.onChanged. Из-за природы JS с единичным циклом событий это означает, что кэш будет обновляться только тогда, когда ваш код не выполняется, но во многих случаях это приемлемо.

Аналогично, если вы хотите распространить изменения в storageCache на реальное хранилище, просто установить storageCache['key'] недостаточно. Вам нужно будет написать set(key, value) прокладку, которую ОБА пишет в storageCache и планирует (асинхронный) chrome.storage.sync.set.

Реализация их остается в качестве упражнения.

Ответ 2

  • chrome.storage.sync.get не имеет возвращенных значений, что объясняет, почему вы получили бы undefined при вызове что-то вроде

    var a = chrome.storage.sync.get({"disableautoplay": true});
    
  • chrome.storage.sync.get также является асинхронным, что объясняет, почему в следующем коде a будет undefined, если вы не получите доступ к нему внутри функция обратного вызова.

    var a;
    window.onload = function(){
        chrome.storage.sync.get({"disableautoplay": true}, function(e){
            // #2
            a = e.disableautoplay; // true or false
        });
        // #1
        a; // undefined
    }
    

Ответ 3

Если вам удастся это решить, вы создадите источник странных ошибок. Сообщения выполняются асинхронно, что означает, что при отправке сообщения остальная часть вашего кода может выполняться до возвращения асинхронной функции. Для этого нет гарантии, поскольку хром является многопоточным, и функция get может задерживаться, т.е. Hdd занят. Использование кода в качестве примера:

var a;
window.onload = function(){
    chrome.storage.sync.get({"disableautoplay": true}, function(e){
    a = e.disableautoplay;
});
}

if(a)
   console.log("true!");
else
   console.log("false! Maybe undefined as well. Strange if you know that a is true, right?");

Итак, будет лучше, если вы используете что-то вроде этого:

chrome.storage.sync.get({"disableautoplay": true}, function(e){
    a = e.disableautoplay;
    if(a)
      console.log("true!");
    else
      console.log("false! But maybe undefined as well");
});

Если вы действительно хотите вернуть это значение, используйте API хранения javascript. Это сохраняет только строковые значения, поэтому вам нужно указать значение перед сохранением и после его получения.

//Setting the value
localStorage.setItem('disableautoplay', JSON.stringify(true));

//Getting the value
var a = JSON.stringify(localStorage.getItem('disableautoplay'));

Ответ 4

Сделайте основную функцию "асинхронной" и сделайте в ней "Обещание" :)

async function mainFuction() {
    var p = new Promise(function(resolve, reject){
        chrome.storage.sync.get({"disableautoplay": true}, function(options){
            resolve(options.disableautoplay);
        })
    });

    const configOut = await p;
    console.log(configOut);
}