与 localforage 的连锁承诺

Chain promises with localforage

我正在为承诺而苦苦挣扎。我通过 .then().then().then().then().then().then().then().then().then().then().then().then() 看到事件链是如何发生的,但我不知道如何让它结束。我希望我能做一个简单的:

 .then(callback(mydata))

但是,我无法让它工作。我正在努力做到这一点。

function doSomethingCallback(theArrayComesBackHere) {
    theArrayComesBackHere.forEach(/*do stuff*/);
}    

button.onclick = () => {
   myobj.getlocalforagedata(doSomethingCallback);
}

myobj = {
   getlocalforagedata: (callback) => {
      var arr = [];
      localForage.keys().then((keys) => {
          keys.forEach((key) => {
              localForage.getItem(key).then(function (results) {
                  arr.push(results);
              });
          });
          callback && callback(arr);
      });
   }
}

请帮我摆脱这种疯狂。

[编辑] 还不错,但是它 returns 一组对象很难读取以供下次使用(你需要一个循环获取值),并且似乎也忘记了 null 值,因此完整而巧妙的解决方案应该更像是:

  const getLocalForageDataByKeys = () => {
    return localForage.keys().then(keys => {
      return Promise.all(keys.map(key => {
        return localForage.getItem(key)
          .then(value => {
            return { [key]: value }
          })
          .catch(error => {
            console.log(error)
            return { [key]: null }
          })
      })).then(arr => {
        return Object.assign(...arr)
      })
    })
  }

用法:

getLocalForageDataByKeys()
  .then(obj => {
    console.log(obj) // { keyA: 'valueA', keyB: 'valueB' }
  }).catch(error => {
    console.log(error) // do sth with error
  })

注:

更好的是,此解决方案使用 ES6 and jsLint 最高质量标准

更多:

localForage 的承诺示例:https://localforage.github.io/localForage/#data-api-getitem

使用承诺:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

使用对象赋值:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

使用扩展运算符:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

这个问题的[以前]接受的答案是使用 explicit promise creation antipattern,这种做法会使代码变得更加复杂和错误。

你可以更干净地完成你想做的事情,就像这样:

function getLocalForageData() {
    return localForage.keys().then(function (keys) {
        return Promise.all(keys.map(function (key) {
            return localForage.getItem(key);
        });
    });
}

用法示例:

getLocalForageData()
    .then(function (values) {
        console.log(values);
    })
    .catch(function (error) {
        console.error(error);
    });

上面得到的是一个值数组,没有匹配到各自的key。如果您希望值与它们的键配对,您可以这样做:

function getLocalForageData() {
    return localForage.keys().then(function (keys) {
        return Promise.all(keys.map(function (key) {
            return localForage.getItem(key)
                .then(function (value) {
                    return { key: key, value: value };
                });
        }));
    });
}

或者您可以分解其中一个内部函数以减少嵌套:

function getLocalForageValueWithKey(key) {
    return localForage.getItem(key)
        .then(function (value) {
            return { key: key, value: value };
        });
}

function getLocalForageData() {
    return localForage.keys().then(function (keys) {
        return Promise.all(keys.map(getLocalForageValueWithKey));
    });
}

无论哪种情况,调用和使用 getLocalForageData 函数的代码都与上面相同。

问题中代码的几个功能不是必需的:

  • new Promise(...): localForage.keys() return 是构成承诺链根的承诺。
  • callback: 通过 return 来自 getlocalforagedata() 的承诺,调用者可以链接 getlocalforagedata().then(...) 避免需要要传递的回调。
  • 外部变量 arr: 正确构造的承诺链可以在没有任何外部变量的情况下收集和交付结果。

以下是必须的:

  • 聚合由 localForage.getItem(key) 编辑的 return 承诺和 return 链中该步骤的承诺。
  • 在等待 localForage.getItem(key) 承诺结算的进一步链式 .then() 中执行测试 if (err.length > 0)if (arr.length > 0)

试试这个:

myobj = {
    getlocalforagedata: () => {
        return localForage.keys()
        .then(keys => {
            // Use `keys.map(...)` to return an array of promises.
            // Then use `Promsie.all()` to aggregate the promises returned by `localForage.getItem(key)`.
            return Promise.all(keys.map(key => localForage.getItem(key)));
        })
        .then(arr => { // an array of results
            // These tests must be performed in a chained `then()`,
            // which will wait for all the individual `localForage.getItem(key)` results (or errors) to be delivered
            if (arr.length > 0) {
                return arr; // deliver a non-empty array
            } else {
                throw new Error('no results'); // throw error down the promise chain's error path.
            }
        });
    }
}

调用如下:

myobj.getlocalforagedata()
.then((results) => {
    // Work with results
})
.catch((error) => {
    // Do whatever with error.
    // Either return some default value or re-throw `error`
});

getlocalforagedata() 可以与回调一起工作,但更好的做法是 return 承诺并允许调用者链接 getlocalforagedata().then(...).