如何在 ES6 生成器中将第一个 yield 的值传递给下一个?

How to pass value from first yield to next ones in ES6 generator?

我搜索了 ES6 生成器的真实案例,但没有找到合适的解释。我试图理解第一个 Promise 场景。

假设我们有 2 个 rest api:getUsergetUserComments

我们不想创建一个线性代码结构来避免嵌套 then 回调函数。所以...

定义模拟 api

function getUser () {
    return new Promise(function(resolve, reject) {
       return setTimeout(function() { resolve({id: 1, label: 'John'}); }, 200);
    });
}

function getUserComments(userId) {
    return new Promise(function(resolve, reject) {
      return setTimeout(function() {resolve({comments: 'comments', userId: userId}); }, 200);
    });
}

定义生成器

function* generator () {
  yield getUser();
  yield getUserComments(userId); // userId represents data from first request / first yield how should I pass it ?!
}

var g = generator();
var first  = g.next(1);
var second = g.next(2);

如何从第一个 yield (yield getUser()) 获取响应并将其传递给下一个 yield (yield getUserComments(userId))?

您可以将产生的结果分配给变量:

The next() method also accepts a value which can be used to modify the internal state of the generator. A value passed to next() will be treated as the result of the last yield expression that paused the generator. (From MDN, Advanced generators)

然后,由于您正在处理 Promises,因此使用 async/await 将其解包为值是有意义的。

试试这个(我稍微修正了 setTimeout 参数):

async function getUser() {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve, 0, {
      id: 1,
      label: 'John'
    })
  })
};

async function getUserComments(user) {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve, 0, {
      ...user,
      comments: 'comments',
    }, 'userComments');
  });
}

function* generator () {
  const user = yield getUser();
  yield getUserComments(user);
}

(async function () {
  const g = generator();
  const user = await g.next().value
  const comments = await g.next(user).value

  console.log(comments)
})()

你可以用一个参数调用next并使用yield获取它。示例:

function* generator () {
  var userId = yield getUser();
  yield getUserComments(userId); // userId represents data from first request / first yield how should I pass it ?!
}

var g = generator();
var first  = g.next();
var userId = getUserId(first);
var second = g.next(userId);

我已经尝试得到您需要的结果。没有 .then(),我无法得到 getUser()

的结果

function getUser () {
    return new Promise(function(resolve, reject) {
       return setTimeout(function() { resolve({id: 1, label: 'John'}); }, 200);
    });
}

function getUserComments(userId) {
    console.log(userId)
    return new Promise(function(resolve, reject) {
      return setTimeout(function() {resolve({comments: 'comments', userId: userId}); }, 200);
    });
}

function* generator () {
  var userId = yield getUser();
  yield getUserComments(userId); // userId represents data from first request / first yield how should I pass it ?!
}

var g = generator();
   
(async function(){
  var first = await g.next();
  var userId = await first.value.then((res)=>{ return res.id });
  g.next(userId);
})();

在某些时候,必须等待或链接 promise (then)。目标是完全防止链接还是仅在实现代码中?否则生成器本身可以进行链接,因此调用代码不必:

function* generator () {
    const u = getUser(), c = u.then(userId => getUserComments(userId));
  yield *[u,c];
}

示例:

function getUser () {
    return new Promise(function(resolve, reject) {
       setTimeout(function() { resolve({id: 1, label: 'John'}); }, 200);
    });
}

function getUserComments(userId) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {resolve({comments: 'comments', userId: userId}); }, 200);
    });
}


function* generator () {
 const u = getUser(), c = u.then(userId => getUserComments(userId));
  yield *[u,c];
}


(async function(){
 var g = generator();
  var first  = await g.next().value;
  var second = await g.next().value;
  console.log(first, second);
})();

对于调用代码,哪个 promise 先 resolved/awaited 并不重要。当然,如果等待后面的步骤,链条必须为前面的步骤完成。例如:

function getUser () {
    return new Promise(function(resolve, reject) {
       setTimeout(function() { resolve({id: 1, label: 'John'}); }, 200);
    });
}

function getUserComments(userId) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {resolve({comments: 'comments', userId: userId}); }, 200);
    });
}


function* generator () {
 const u = getUser(), c = u.then(userId => getUserComments(userId));
  yield *[u,c];
}


(async function(){
 var g = generator();
  var prom  = g.next().value;  
  var second = await g.next().value;
  
  console.log(await prom, second);
})();