ожидание события indexdb в асинхронной функции - PullRequest
0 голосов
/ 16 октября 2018

Я пытаюсь вернуть пользовательский объект из асинхронной функции, которая работает как оболочка для пута, используя indexdb.

Используя Promises, это легко.Однако использование async / await стало более сложным ...

const set = async (storeName, key, value) => {


    if (!db)
        throw new Error("no db!");

    try {

        const result = {};

        let tx = db.transaction(storeName, "readwrite");
        let store = tx.objectStore(storeName);
        let r = store.put({ data: key, value: value });

        console.log(r);

        r.onsuccess = async () => {
            console.log('onsuccess');
            result.something = true;
        }
        r.onerror = async () => {
            console.log('onerror');
            result.something = false;
        }

        await r.transaction.complete;  // ok... this don't work

        // how can I await until onsuccess or onerror runs?

        return result;

    } catch (error) {
        console.log(error);
    }
}

Идея состоит в том, чтобы вернуть составной объект ... однако все мои попытки завершаются неудачно, так как onsuccess выполняется после возврата результата.

Я много гуглил и не мог найти способ правильно ожидать события onsuccess / onerror.

Я знаю, что вернуть Обещание проще, так как решение (результат) закончится возвращением того, что я хочу ...но я пытаюсь научиться делать тот же код, используя async / await .

Большое спасибо,

Ответы [ 3 ]

0 голосов
/ 16 октября 2018

Попробуйте:

function set(db, storeName, key, value) {
  return new Promise((resolve, reject) => {
    let result;
    const tx = db.transaction(storeName, 'readwrite');
    tx.oncomplete = _ => resolve(result);
    tx.onerror = event => reject(event.target.error);
    const store = tx.objectStore(storeName);
    const request = store.put({data: key, value: value});
    request.onsuccess = _ => result = request.result;
  });
}

async function callIt() {
  const db = ...;

  const result = await set(db, storeName, key, value);
  console.log(result);
}

Редактировать, поскольку вы настаиваете на использовании асинхронного квалификатора для функции set, вы можете сделать это вместо этого.Обратите внимание, я нахожу это довольно глупо:

async function set(db, storeName, key, value) {
  // Wrap the code that uses indexedDB in a promise because that is 
  // the only way to use indexedDB together with promises and 
  // async/await syntax. Note this syntax is much less preferred than 
  // using the promise-returning function pattern I used in the previous 
  // section of this answer.
  const promise = new Promise((resolve, reject) => {
    let result;
    const tx = db.transaction(storeName, 'readwrite');
    tx.oncomplete = _ => resolve(result);
    tx.onerror = event => reject(event.target.error);
    const store = tx.objectStore(storeName);
    const request = store.put({data: key, value: value});
    request.onsuccess = _ => result = request.result;
  });

  // We have executed the promise, but have not awaited it yet. So now we 
  // await it. We can use try/catch here too, if we want, because the 
  // await will translate the promise rejection into an exception. Of course, 
  // this is also rather silly because we are doing the same thing as just 
  // allowing an uncaught exception to exit the function early.
  let result;
  try {
    result = await promise;
  } catch(error) {
    console.log(error);
    return;
  }

  // Now do something with the result
  console.debug('The result is', result);
}
0 голосов
/ 20 октября 2018

В конечном итоге вы завернете IDB в библиотеку «друг-обещание», но для своих конкретных нужд вы можете использовать что-то вроде этого:

function promiseForTransaction(tx) {
  return new Promise((resolve, reject) => {
    tx.oncomplete = e => resolve();
    tx.onabort = e => reject(tx.error);
  });
}

И затем в своем коде вы сможете писать такие вещиas:

await promiseForTransaction(r.tx);

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

0 голосов
/ 16 октября 2018

Я не могу подтвердить это прямо сейчас, но я думаю, что это должно быть await tx.complete вместо await r.transaction.complete;.

Но общее решение, которое будет работать, даже если API не будет поддерживать Promises напрямую, будетчтобы обернуть new Promise вокруг onsuccess и onerror и использовать await, чтобы дождаться разрешения Обещания, а в ваших onsuccess и onerror вы затем вызовете функцию resolve:

const set = async (storeName, key, value) => {


  if (!db)
    throw new Error("no db!");

  try {

    const result = {};

    let tx = db.transaction(storeName, "readwrite");
    let store = tx.objectStore(storeName);
    let r = store.put({
      data: key,
      value: value
    });

    console.log(r);

    await new Promise((resolve, reject) => {
      r.onsuccess = () => {
        console.log('onsuccess');
        result.something = true;
        resolve()
      }

      r.onerror = () => {
        console.log('onerror');
        result.something = false;
        // I assume you want to resolve the promise even if you get an error
        resolve()
      }
    })

    return result;
  } catch (error) {
    console.log(error);
  }
}

Я хотел бы изменить его на:

try {
  await new Promise((resolve, reject) => {
    r.onsuccess = resolve
    r.onerror = reject
  })

  console.log('success');
  result.something = true;
} catch(err) {
  console.log('error');
  result.something = false;
}
...