Javascript承诺序列

Mar*_*eby 6 javascript promise ecmascript-6

我想在序列中处理许多承诺.我在下面有一段工作代码,但我想知道我是否过度复杂了承诺的链接.我似乎正在创造大量新的封闭装置,我想知道我是否遗漏了一些东西.

有没有更好的方法来编写此函数:

'use strict';
addElement("first")
.then(x => {return addElement("second")})
.then(x => { return addElement("third")})
.then(x => { return addElement("fourth")})   

function addElement(elementText){
    var myPromise = new Promise(function(resolve,reject){
        setTimeout(function(){
            var element=document.createElement('H1');
            element.innerText = `${elementText} ${Date.now()}`;
            document.body.appendChild(element);
            resolve();
        }, Math.random() * 2000);
    });
return myPromise;
}
Run Code Online (Sandbox Code Playgroud)

Tha*_*you 6

@TheToolBox有一个很好的答案.

只是为了好玩,我将向您展示一种替代技术,它使用从协同程序中获取灵感的生成器.

Promise.prototype.bind = Promise.prototype.then;

const coro = g => {
  const next = x => {
    let {done, value} = g.next(x);
    return done ? value : value.bind(next);
  }
  return next();
}
Run Code Online (Sandbox Code Playgroud)

使用它,您的代码将如下所示

const addElement = elementText =>
  new Promise(resolve => {
    setTimeout(() => {
      var element = document.createElement('H1');
      element.innerText = `${elementText} ${Date.now()}`;
      document.body.appendChild(element);
      resolve();
    }, Math.random() * 2000);
  });

coro(function* () {
  yield addElement('first');
  yield addElement('second');
  yield addElement('third');
  yield addElement('fourth');
}());
Run Code Online (Sandbox Code Playgroud)

使用承诺生成器可以做一些非常有趣的事情.它们在这里并不是很明显,因为你的addElement承诺并没有解决任何实际价值.


如果你真的resolve有些价值观,你可以做点什么

// sync
const appendChild = (x,y) => x.appendChild(y);

// sync
const createH1 = text => {
  var elem = document.createElement('h1');
  elem.innerText = `${text} ${Date.now()}`;
  return elem;
};

// async
const delay = f =>
  new Promise(resolve => {
    setTimeout(() => resolve(f()), Math.random() * 2000);
  });

// create generator; this time it has a name and accepts an argument
// mix and match sync/async as needed
function* renderHeadings(target) {
  appendChild(target, yield delay(() => createH1('first')));
  appendChild(target, yield delay(() => createH1('second')));
  appendChild(target, yield delay(() => createH1('third')));
  appendChild(target, yield delay(() => createH1('fourth')));
}

// run the generator; set target to document.body
coro(renderHeadings(document.body));
Run Code Online (Sandbox Code Playgroud)

值得注意,createH1并且appendChild是同步功能.这种方法有效地允许您将正常功能链接在一起,并模糊同步和异步之间的界限.它的执行/行为与您最初发布的代码完全相同.

所以,是的,最后一个代码示例可能会更有趣.


最后,

协程相对于.then链接的一个明显优势是所有已解析的promise都可以在同一范围内访问.

比较.then链......

op1()
  .then(x => op2(x))
  .then(y => op3(y))    // cannot read x here
  .then(z => lastOp(z)) // cannot read x or y here
Run Code Online (Sandbox Code Playgroud)

对于协程......

function* () {
  let x = yield op1(); // can read x
  let y = yield op2(); // can read x and y here
  let z = yield op3(); // can read x, y, and z here
  lastOp([x,y,z]);     // use all 3 values !
}
Run Code Online (Sandbox Code Playgroud)

当然有使用承诺的解决方法,但哦,男孩,它变得难看快...


如果您对以这种方式使用发电机感兴趣,我强烈建议您检查合作项目.

而这里的一篇文章,回调VS协同程序,从创作者合作,@tj.

无论如何,我希望你学习其他一些技巧^ __ ^


The*_*Box 3

您的代码看起来接近您可以在此处获得的最佳代码。承诺可能是一种奇怪的结构,需要习惯,尤其是编写承诺化代码通常最终会将一个函数嵌入另一个函数中。正如您在这里所看到的,这是一个非常常用的措辞。只能做出两个风格上的改变。首先,myPromise这是不必要的,只会添加令人困惑的额外代码行。直接返回promise就更简单了。其次,您可以在一开始就使用函数绑定来简化您的调用。它可能不在函数本身内部,但它确实消除了几个闭包。两项更改如下所示:

'use strict';
addElement("first")
.then(addElement.bind(null,"second"))
.then(addElement.bind(null,"third"))
.then(addElement.bind(null,"fourth"))   

function addElement(elementText){
    return new Promise(function(resolve,reject){
        setTimeout(function(){
            var element=document.createElement('H1');
            element.innerText = `${elementText} ${Date.now()}`;
            document.body.appendChild(element);
            resolve();
        }, Math.random() * 2000);
    });
}
Run Code Online (Sandbox Code Playgroud)

值得指出的是,如果你愿意稍微重组一下,就会形成一个稍微更有吸引力的设计:

'use strict';
var myWait = waitRand.bind(null,2000);
myWait
  .then(addElement.bind(null, "first"))
  .then(myWait)
  .then(addElement.bind(null, "second"))
  .then(myWait)
  .then(addElement.bind(null, "third"))

function waitRand(millis) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, Math.random() * millis);
  }
}

function addElement(elementText) {
  var element = document.createElement('h1');
  element.innerText = `${elementText} ${Date.now()}`;
  document.body.appendChild(element);
}
Run Code Online (Sandbox Code Playgroud)

为了清晰起见,这会牺牲承诺链的长度,并且嵌套级别也会稍微少一些。