我是在正确地链接 Promise 还是犯了罪?

Am I chaining Promises correctly or committing a sin?

我已经很长时间没有使用 Javascript 了,所以现在 promises 对我来说是一个新概念。我有一些操作需要 多个异步调用 但我想将其视为一个事务,如果前面的步骤失败,则步骤不会执行。目前我通过嵌套链接承诺,我想return向调用者承诺。

阅读 Mozilla 的 Using Promises 指南的链接部分后,我不确定我所做的是否正确或等同于 "callback pyramid of doom"。

有没有更简洁的方法来做到这一点(除了在每个 then 中链接一个警卫检查)?我是否相信在 Mozilla 的示例中它会执行每个链式 then 即使出现错误?

myfunction(key) => {
    return new Promise((outerResolve, outerReject) => {
        return new Promise((resolve, reject) => {
            let item = cache.get(key);
            if (item) {
                resolve(item);
            } else {
                //we didnt have the row cached, load it from store   
                chrome.storage.sync.get(key, function (result) {
                    chrome.runtime.lastError
                        ? reject({ error: chrome.runtime.lastError.message })
                        : resolve(result);
                });
            }
        }).then((resolve) => {
            //Now the inner most item is resolved, we are working in the 'outer' shell
            if (resolve.error) {
                outerReject(resolve);
            } else {
                //No error, continue
                new Promise((resolve, reject) => {
                    chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
                        chrome.runtime.lastError
                            ? reject({ error: chrome.runtime.lastError.message })
                            : resolve(result);
                    });
                }).then((resolve) => {
                    //finally return the result to the caller
                    if (resolve.error) {
                        outerReject(resolve);
                    } else {
                        outerResolve(resolve);
                    }
                });
            }
        });
    });
}

当抛出异常时,不会执行后续的 then 语句(直到 catch)。此外,.then returns 一个 Promise,因此您无需创建额外的外部 Promise。

试试这个例子:

var p = new Promise((resolve, reject) => {
    console.log('first promise, resolves');
    resolve();
})
.then(() => {
    throw new Error('Something failed');
})
.then(() => {
    console.log('then after the error');
    return('result');
});

p.then(res => console.log('success: ' + res), err => console.log('error: ' + err));

您不会在控制台中看到 "then after the error",因为那是在抛出异常之后发生的。但是如果你注释 throw 语句,你将在 Promise 中得到你期望的结果。

我不确定我是否完全理解你的例子,但我认为它可以像这样简化:

myfunction(key) => {
    return new Promise((resolve, reject) => {
        let item = cache.get(key);
        if (item) {
            resolve(item);
        } else {
            //we didnt have the row cached, load it from store   
            chrome.storage.sync.get(key, function (result) {
                chrome.runtime.lastError
                    ? throw new Error(chrome.runtime.lastError.message)
                    : resolve(result);
            });
        }
    }).then((previousData) => {
        // keyBasedOnPreviousData is calculated based on previousData
        chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
            chrome.runtime.lastError
                ? throw new Error(chrome.runtime.lastError.message)
                : return result;
        });
    });
}

有点乱。这是我重写的尝试。尽量避免的一件好事是 new Promise().

function chromeStorageGet(key) {    
  return new Promise( (res, rej) => {

     chrome.storage.sync.get(key, result => {   
       if (chrome.runtime.lastError) {
         rej(new Error(chrome.runtime.lastError.message))
       } else {
         res(result)
       }
     }); 

  });  
});


function myfunction(key) {

   const item = cache.get(key) ? Promise.resolve(cache.get(key)) : chromeStorageGet(key);
   return item.then( cacheResult => {
     return chromeStorageGet(keyBasedOnPreviousData);
   });

}

为什么要避免new Promise()

这样做的原因是您想用 then() 完成每一步。如果 any 的承诺中发生任何错误,链中的每个承诺都将失败,并且任何后续的 then() 都不会被执行 until有一个 catch() 处理程序。

许多承诺 based-code 不需要错误处理程序,因为 promise-based 函数总是 return 承诺和异常应该全部返回给调用者,直到有有用的事情要做错误处理。

请注意,这 2 条规则的例外情况在我的 chromeStorageGet 函数中。这里有几点说明:

  • new Promise 是一种将回调代码转换为承诺代码的快速简便的方法。
  • 为此 callback-based 代码创建一个小的转换层通常是个好主意。如果您在其他地方需要 chrome.storage.sync,也许可以创建一个小实用程序来实现其所有功能。
  • 如果只有1个'flow',可以直接用一系列then()来完成,但有时需要有条件地做其他事情。只需将这些复杂的操作拆分为多个不同的函数就可以真正提供帮助。

但是这个:

const result = condition ? Promise.resolve() : Promise.reject();

几乎总是优先于:

const result = new Promise( (res, rej) => { 
  if (condition) {
    res();
  } else { 
    rej();
  }
}