如何在保持纯粹的同时使用Promise和Cursors?

时间:2017-11-10 04:22:48

标签: javascript asynchronous promise immutability indexeddb

在我的课程中,我一直在学习indexeddb以及如何处理异步。为了扩展自己并学习更多知识,我一直在尝试在我的代码中使用函数范例。

之前我一直在使用游标,但我意识到我的代码不是完全不可变的或无状态的,这让我很困扰。我想知道是否有办法使用游标而不必求助于将元素推送到数组。

目前,我使用的是:

async function getTable(){
   return new Promise(function(resolve, reject){
      const db = await connect();
      const transaction = await db.transaction(["objectStore"], "readonly");
      const store = await transaction.objectStore("objectStore");
      var myArray = [];
      store.openCursor().onsuccess = function(evt) {                   
         var cursor = evt.target.result;
         if (cursor) {
            myArray.push(cursor.value);
            //I don't want to use push, because it's impure. See link:
            cursor.continue();
         } else {
            resolve(myArray);
      }
   }
}

//link: https://en.wikipedia.org/wiki/Purely_functional_programming

它工作正常。但它不纯粹,它使用推。如果有的话,我想学习另一种方法。

谢谢!

1 个答案:

答案 0 :(得分:0)

你可以根据函数式编程的精神做一些事情,但在JavaScript中可能不值得。

例如,要实现不变性数组,至少在精神上,您只需在每次要向数组添加元素时创建并返回一个新数组。我想如果我正确地回忆起我的方案,那么这个函数被称为cons

function push(array, newValue) {
  const copy = copyArray(array);
  copy.push(newValue);
  return copy;
}

function copyArray(array) {
  const copy = [];
  for(const oldValue of array) {
    copy.push(oldValue);
  }
  return copy;
}

// Fancy spread operator syntax implementation if you are so inclined
function copyArray2(inputArray) {
  return [...inputArray];
}

现在不是改变输入数组,而是创建它的修改副本。请记住,这绝对是可怕的表现,你可能永远不会想在真正的应用程序中这样做。

你可以更进一步,并使用一些基于堆栈的方法。同样,这是非常糟糕的,但它基本上会创建一个返回函数的push函数。当你追加项目时,堆栈的大小会增加,然后当你展开它时,它会展开成一个值数组。

我的第二点是,您可以通过使用indexedDB的更新,记录不足的功能来完全避免此阵列构建。具体而言,IDBObjectStore.prototype.getAll。这个函数会为你创建不透明的数组,如果它是不透明的,你永远不会知道它的抽象中隐藏的任何FP反模式,因此不会破坏规则。

function getTable(){
  return new Promise(function(resolve, reject){
    const db = await connect();
    const transaction = await db.transaction(["objectStore"], "readonly");
    const store = await transaction.objectStore("objectStore");
    const request = store.getAll();
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
 }
}

我的第三点很简单,使用db.transaction("objectStore", "readonly");代替db.transaction(["objectStore"], "readonly");。数组参数是可选的,最好保持简单。

我的第四点很简单,使用db.transaction("objectStore");代替db.transaction(["objectStore"], "readonly");。 "只读"是事务的默认模式,因此无需指定它。通过不指定参数可以充分清楚地传达代码的目的,省略参数的详细程度较低。

我的第五点是你在函数定义中使用async说明符(?)。你不需要在这里使用它。您有一个同步函数返回Promise的实例。如果有的话,指定异步会导致对代码正在做什么的混淆。相反,您可能希望在使用函数时使用async限定符。

我的第六点是你在致电connect()时违反了一些FP原则。什么是connect连接到?隐含的全球状态。这完全违反了函数式编程的精神。因此,您的连接参数必须是函数本身的参数,因此您不必依赖于使用哪个数据库的隐含知识。

我的第七点是您正在使用数据库。功能程序员在数据库,或任何I / O或与外界的交互方面存在很多问题,他们似乎想假装没有这样的东西。因此,如果您想使用功能方法,您可能根本就不应该使用数据库。

我的八点是,在承诺(呼叫和等待connect)内的联系绝对是一种反模式。目标是链接承诺,以便一个接一个地开始。呼叫者必须呼叫连接并then呼叫getTable,或getTable必须呼叫connect,然后执行其余承诺。

我的第九点是我甚至不确定这是如何执行的。传递给Promise构造函数的执行程序函数未被限定为async。因此,您在非限定函数中使用await修饰符。这应该是一个错误。从技术上讲,承诺吞噬了异常,这意味着这个承诺应该总是拒绝。

我的第十点是你到处使用async。我不知道发生了什么,除非你的connect函数返回某种包装器库,但对IDBDatabase.prototype.transactionIDBTransaction.prototype.objectStore的调用是同步的。你等待他们的原因毫无意义。他们不回报承诺。'

我的第十一点是你没有注意到错误。发生错误时不会回叫request.onsuccess。这可能会导致你的承诺永远不会解决。您还需要考虑失败案例。

我的第12点是你似乎错过了onsuccess处理函数的右括号。我不确定这个代码是如何成功解释的。