javascript - 如何渲染异步 forEach 'find' 函数数组的结果?

标签 javascript node.js asynchronous generator ecmascript-6

这是我的简单任务:通过 id 数组查找图像并将图像值渲染到模板中。

router.get('/gallery', function(req, res) {
  var images = [];
  imagesIds.forEach(function(eachImageId) {
    Images.findById(eachImageId).exec(function(findImageErr, foundImage) {
      if (foundImage) {
        images.push(foundImage);
      }
    });
  });
  res.render('gallery', {
    images: images
  });
});

问题是“res.render”函数不等待“findById”函数完成。 “images”数组始终变为“[]”为空。

我尝试使用生成器但不知道如何实现。

如果有人可以在没有库(比如q)的情况下解释会更好。因为我想深入了解generator如何处理这个问题。

最佳答案

生成器允许编写类似同步的函数,因为它们可以停止其执行并稍后恢复。

I guess you already read some articles like this and know how to define generator function and use them.

您的异步代码可以表示为带有神奇 yield 关键字的简单迭代器。生成器函数将运行并在此停止,直到您使用方法 next() 恢复它。

function* loadImages(imagesIds) {
    var images = [], image;
    for(imageId of imagesIds) {
        image = yield loadSingleImage(imageId);
        images.push(image);
    }
    return images;
}

因为存在循环,所以函数将通过每个 next() 执行循环,直到遍历完所有 imagesId。最后会执行return语句,你会得到图像

现在我们需要描述图像加载。我们的生成器函数需要知道当前图像何时加载并且可以开始加载下一个图像。所有现代 javascript 运行时(node.js 和最新的浏览器)都具有 native Promise 对象支持,我们将定义一个返回 Promise 的函数,如果找到它,它最终将通过图像进行解析。

function loadSingleImage(imageId) {
    return new Promise((resolve, reject) => {
        Images.findById(imageId).exec((findImageErr, foundImage) => {
            if (foundImage) {
               resolve(foundImage)
            } else {
               reject();
            }
        });
    });
}

我们有两个函数,一个用于单个图像加载,第二个用于将它们组合在一起。现在我们需要一个调度程序来将控制从一个函数传递到另一个函数。由于您不想使用库,因此我们必须自己实现一些帮助程序。

它是 spawn function 的较小版本,这可以更简单、更好理解,因为我们不需要处理错误,而只需忽略丢失的图像。

function spawn(generator) {
    function continuer(value) {
         var result = generator.next(value);
         if(!result.done) {
             return Promise.resolve(result.value).then(continuer);
         } else {
             return result.value;                 
         }
    }
    return continuer();
}

result.done 不为 true 时,该函数在 continuer 函数中执行生成器的递归调用。一旦得到,就意味着生成已经成功完成,我们可以返回我们的值。

最后,将所有内容放在一起,您将获得以下用于图库加载的代码。

router.get('/gallery', function(req, res) {
    var imageGenerator = loadImages(imagesIds);
    spawn(imageGenerator).then(function(images) {
        res.render('gallery', {
            images: images
        });
    });
});

现在,loadImages 函数中有一些伪同步代码。我希望它有助于理解发电机的工作原理。

Also note that all images will be loaded sequently, because we wait asynchronous result of loadSingleImage call to put it in array, before we can go to the next imageId. It can cause performance issues, if you are going to use this way in production.

相关链接:

关于javascript - 如何渲染异步 forEach 'find' 函数数组的结果?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32430821/

相关文章:

session - 在 nodejs/redis 中保存 tcp session ?

node.js - Sequelize 和响应请求 GraphQL

multithreading - 使用 Clojure core.async 限制进程

javascript - 如何转换返回 promise 使用异步/等待的 redux-thunk 操作?

javascript - 如何在 jQuery DataTables 中对文件大小进行排序

javascript - 页面加载时触发 JS 函数不起作用

javascript - jquery 同时淡入淡出

javascript - 为什么返回响应的格式很奇怪?

javascript - 循环遍历数组匹配变量并分配执行条件

c# - async/await 创建新线程,输出窗口显示