【问题标题】:ECMAScript 2015 call Promises sequentiallyECMAScript 2015 按顺序调用 Promise
【发布时间】:2017-07-22 07:38:04
【问题描述】:

我正在尝试重写一个同步连接以使用 Promise,但我有点坚持。我有一个函数可以调用不同的例程 ABC,具体取决于参数和结果:

const worker = (v, r, ok, er)=>{
  if(v > 10) {
    ok(r)
  } else {
    er(r)
  }
};

const A = v=>{let r = null; worker(v, 'a', _r=>r=_r, ()=>{});return r};
const B = v=>{let r = null; worker(v, 'b', _r=>r=_r, ()=>{});return r};
const C = v=>{let r = null; worker(v, 'c', _r=>r=_r, ()=>{});return r};

const mainSync = (obj)=>{
  let result = null;
  if(obj.a) {
    result = A(obj.a);
  }
  if (!result && obj.b) {
    result = B(obj.b);
  }
  if (!result && obj.c) {
    result = C(obj.c);
  }
  return result;
}

https://repl.it/JcjE/0 与同步 ABC 工作正常:

mainSync({a:4}) === null;
mainSync({a:4, b:14}) === 'b';
mainSync({a:14, b:14}) === 'a';
mainSync({b:4, c:14}) === 'c';
// etc

现在 ABC 成为 Promise:

const worker = (v, r, ok, er)=>{
  if(v > 10) {
    ok(r)
  } else {
    er(r)
  }
};

const A = v=>new Promise((ok, er)=>worker(v, 'a', ok, er));
const B = v=>new Promise((ok, er)=>worker(v, 'b', ok, er));
const C = v=>new Promise((ok, er)=>worker(v, 'c', ok, er));

我不太确定如何处理它:

const mainAsync = (obj)=>{
    // what todo here?
}

我很高兴 mainAsync 返回 Promise 本身,比如

mainAsync({a:4}).then(r=>r === null);
mainAsync({a:4, b:14}).then(r=>r === 'b');
mainAsync({a:14, b:14}).then(r=>r === 'a');
mainAsync({b:4, c:14}).then(r=>r === 'c');

问题是对B 的调用取决于A 的结果,而对C 的调用取决于AB 的结果,目前还没有async/await 可用。

我尝试了我的幼稚方法https://repl.it/Jcjw/0,但它很糟糕,并且在现实生活中不太适用。

PS:如果可能的话,我正在寻找 vanilla javascript,并且知道类似的问题,例如

等等,但不知道如何将它们应用到我的案例中。

【问题讨论】:

  • 你为什么要尝试使用 Promise 进行同步操作?这只是一种学习努力吗?因为没有实际理由将 Promise 与纯同步操作一起使用 - 这只会使代码变得比需要的复杂。
  • 不,这是一个很实际的问题。函数 A B C 已更改,现在我必须调整我的代码才能使用它们。
  • 我还是不明白。如果所有代码都是同步的,就没有理由使用 Promise。就像所有正常的同步代码一样,只需从函数中返回一个值。
  • 对不起,如果我不清楚,但功能 A、B 和 C 超出了我的控制范围。在新版本中,它们变成了承诺。
  • 是的,一点都不清楚。请编辑您的问题以明确说明。此外,您不会“调用承诺”,以免措辞混乱。您调用返回承诺的函数。 Promise 是一个对象,它代表一些未来的异步结果(或错误)——它不是一个函数。

标签: javascript node.js ecmascript-6


【解决方案1】:

要按顺序调用 Promise,您可以在 .then 回调中调用下一个。您的条件 (if (!result && ...)) 很容易翻译:

function mainAsync(obj) {
  return (obj.a ? A(obj.a) : Promise.resolve())
    .then(result => !result && obj.b ? B(obj.b) : result)
    .then(result => !result && obj.c ? C(obj.c) : result);
}

如果您需要为许多属性执行此操作,那么您可以通过使用查找表和循环(在本例中为Array#reduce)来避免重复自己太多:

const funcs = {
  a: A,
  b: B,
  c: C,
};
const props = ['a', 'b', 'c'];

function mainAsync(obj) {
  return props.reduce(
    (promise, prop) => promise.then(
      result => !result && obj[prop] ? funcs[prop](obj[prop]) : result
    ).catch(() => Promise.resolve(null)),
    Promise.resolve(null)
  );
}

【讨论】:

  • 问题是我在参数中并不总是有obj.a,需要从B开始。
  • 谢谢,我想我需要对obj 的其他属性应用相同的逻辑,因为它们都是可选的。我明白了,看起来不错。
  • 太棒了,查找的想法很棒。感谢磨坊。
【解决方案2】:

根据您尝试定位的浏览器类型,您肯定可以使用 chrome 中的async/await 功能。

function promiseA(){return Promise.resolve(20)}
function promiseB(arg){return Promise.resolve(arg * 2)}
function promiseC(arg){return Promise.resolve(arg + 10)}

(async function(){
  let a = await promiseA();
  console.log(a)
  let b = await promiseB(a);
  console.log(b)
  let c = await promiseC(b);
  console.log(c)
})();

【讨论】:

  • 注意这是ES7(所以它可能不属于ES2015条件)
  • @Jonasw 我注意到这个有一个node标签,并且新版本的node现在支持async/await
  • 是的,一切都很好,只是想指出这一点,可能对 OP 有用
  • 是的,我想,我还想在新版本的节点中指出它。
  • @Jonasw:async/await 是 ES2017(今年发布)的一部分,而不是 ES7 (ES2016)。
【解决方案3】:
mainAsync = mainSync;

或者更简单:

function mainAsync({a,b,c}){
  if(c) return C(c);
  if(b) return B(b);
  if(a) return A(a);
}

如果您希望在返回之前完成所有承诺:

function mainAsync({a,b,c}){
 var promises=[];
  if(a) promises.push(A(a));
  if(b) promises.push(B(b));
  if(c) promises.push(C(c));
  return Promise.all(promises).then(val=>val.pop())
}

mainAsync({a:1,b:2,c:3});

【讨论】:

  • 看起来很有希望 =),但不会承诺。全部调用所有 3 个例程,即使 A 返回结果?它们非常昂贵,这是从最便宜的 A 开始依次调用它们以限制对最昂贵的 C 的调用次数的主要原因。
  • @alex blex 是的。我对你的用例不放心。
【解决方案4】:

如果不能使用 ES7 转译器(用于 async/await),您可以使用 ES2015 generators 实现异步控制流。这是一个简单的示例实现:

function *main() {
  let a = yield Promise.resolve(5);
  let b = yield Promise.resolve(a + 10);
  let c = yield Promise.resolve(b + 15);

  // place the rest of your code here
  console.log(c); // prints 30
}

function execAsync(generator, previousValue) {
  const nextValue = generator.next(previousValue);
  if (!nextValue.done) {
    let promise = nextValue.value;
    // FIXME: promise rejection is not handled
    promise.then(function (value) {
      execAsync(generator, value);
    });
  }
}

// ...    

// start the execution
execAsync(main());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 1970-01-01
    • 2019-10-09
    • 2015-07-27
    • 2013-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多