вернуть ответ от асинхронного вызова в стандартном цикле for - PullRequest
0 голосов
/ 28 октября 2019

После прочтения Как вернуть ответ от асинхронного вызова? от Феликса Клинга, я все еще не понимаю, как я могу вернуть значение из асинхронного обратного вызова.

Моя цель: преобразовать статическое изображение в base64 один раз и сохранить это изображение в indexDB до тех пор, пока indexDB не выдаст какую-либо ошибку хранения.

Я использую этот асинхронный idb модуль npm

// init the idb store
const initIDB = async () => {

  const db = await openDB('db', 1, {
    upgrade(db) {
      db.createObjectStore('tempStore', { keyPath: 'id', autoIncrement: true });
    },
  });
  const tx = db.transaction('tempStore', 'readwrite');
  await overloadIDB(tx.store);
  await tx.done;


  return true;
};
// random number generator
const getRandomArbitrary = (min, max) => Math.random() * (max - min) + min;

// function will overload the idb
const overloadIDB = async (store) => {
  const imgurl = "someLocalImage.png";
  const promises = [];
  return toDataURL(imgurl, async (s) => {
    for (let i = 0; i < 10; i++) {
      if (i > 0 && i % 100 === 0) console.log('A set done');
      try {
        const num = Math.round(getRandomArbitrary(1, 1000000));
        const data = {
          id: num,
          img: s,
        };
        store.add(data);
      } catch (e) {
        console.log(e.toString());
        console.dir(e);
        break;
      }
    }
    console.log('Done');
  });
};

// convert image to base64
const toDataURL = (url, callback) => {
  const xhr = new XMLHttpRequest();
  xhr.onload = () => {
    const reader = new FileReader();
    reader.onloadend = () => {
      callback(reader.result);
    };
    reader.readAsDataURL(xhr.response);
  };
  xhr.open('GET', url);
  xhr.responseType = 'blob';
  xhr.send();
};

В идеале я хотел бы вернуть значение из функции обратного вызова toDataURL's и использовать этот результат в цикле for, но я всегда получаю undefined, что имеет смысл из-заасинхронное поведение.

Приведенный выше код не может выполнить транзакцию store.add(data) несколько раз и завершается с ошибкой i = 0.

Я попытался обернуть toDataURL new Promise(resolve, reject) примерно так

const toDataURL = (url, callback) => new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = 'blob';
  xhr.onload = () => {
    const reader = new FileReader();
    reader.onloadend = () => {
      resolve(callback(reader.result));
    };
    reader.readAsDataURL(xhr.response);
  };
  xhr.send();
});

, а затем использовать Promise.all для разрешения массива магазинов, например

const overloadIDB = async (store) => {
  const imgurl = 'someLocalImage.png';
  const promises = [];
  return toDataURL(imgurl, async (s) => {
    console.log('s :', s);
    for (let i = 0; i < 10; i++) {
      if (i > 0 && i % 100 === 0) console.log('A set done');
      try {
        const num = Math.round(getRandomArbitrary(1, 1000000));
        const data = {
          id: num,
          img: s,
        };
        promises.push(store.add(data));
      } catch (e) {
        console.log(e.toString());
        console.dir(e);
        break;
      }
    }
    await Promise.all(promises);
    console.log('Done');
  });
};

но возвращает ошибку Failed to execute 'add' on 'IDBObjectStore': The transaction has finished.

На данный момент я думаю, что мой подход неверен, но я не уверен, как я могу это исправить. Кто-нибудь может указать на какое-то решение, пожалуйста?

1 Ответ

0 голосов
/ 30 октября 2019

Вы не можете выполнять асинхронные операции в середине операций indexedDB. Полностью выполните выборку, затем подключитесь, создайте транзакцию и сохраните результат.

...