链接承诺强制异步

时间:2017-01-15 03:40:31

标签: javascript node.js es6-promise

我使用promises来获取大量的图片专辑,并从该专辑中提取随机样本。我已设法请求所有相册,然后将图像链接推送到对象数组。

现在我想打印出那个阵列,但只有在我实际填充它之后。每当我在末尾添加.then()时,它只打印出初始化的空数组。

我该怎么做才能强制异步,只有在数组填满后才打印它。 (我把它打印在底部)



let findImagesCatalyst = new Promise(function(resolve, reject) {
    //url options 
    const options = {
      url: 'https://api.imgur.com/3/gallery/hot/time/',
      headers: {
        "Authorization": "Client-ID xxxx"
      }
    };

    //inital request
    request(options, function(err, res, body) {
      //parse the response
      body = JSON.parse(body)
        //access the data in the response
      const responseData = body.data;
      //filter only those with image counts great than 50
      const largeAlbums = responseData.filter(findDumps)
        //test to see if a dump is present
      if (largeAlbums.length > 0) {
        largeAlbums.forEach(function(i) {})
        resolve(largeAlbums)
      } else {
        reject()
      }

    })

  })
  //if successful in finding a dump, then go through them and find their albumIds
  .then(function(largeAlbums) {
    let dumpIds = largeAlbums.map(index => index.id)
    return dumpIds;
  })
  //with the album/dump ids, get each of them with a new request
  .then(function(dumpIds) {
    //for each of the dumpIds create the needed url using ES6  and then request it. 
    dumpIds.forEach(function(i) {
      const albumUrlOptions = {
          url: `https://api.imgur.com/3/album/${i}/images`,
          headers: {
            "Authorization": "Client-ID xxxx"
          }
        }
        //make a request to each of the albums/dumps
      request(albumUrlOptions, function(err, res, body) {
        body = JSON.parse(body)
        const responseData = body.data
          //pick one sample image from the album/dump
        let sampleImage = responseData[randomSelector(responseData.length)].link;
        dumps.push({
          "dump": i,
          'sample': sampleImage
        })

      })
    })

    return dumps;
  })

.then(function(dumps) {
  console.log(dumps)
})




3 个答案:

答案 0 :(得分:1)

你是第二个。然后应该返回(承诺的)请求的Promise.all

.then(function(dumpIds) {
    //for each of the dumpIds create the needed url using ES6  and then request it. 
    return Promise.all(dumpIds.map(function(i) {
        const albumUrlOptions = {
            url: `https://api.imgur.com/3/album/${i}/images`,
            headers: {
                "Authorization": "Client-ID xxxx"
            }
        };
        return new Promise((resolve, reject) => {
            //make a request to each of the albums/dumps
            request(albumUrlOptions, function(err, res, body) {
                body = JSON.parse(body)
                const responseData = body.data
                    //pick one sample image from the album/dump
                let sampleImage = responseData[randomSelector(responseData.length)].link;
                resolve({
                    "dump": i,
                    'sample': sampleImage
                });
            });
        });
    }))
})

当你使用具有非常好的ES2015 +实现的node.js时,你可以简化(在我看来)你的代码,首先,创建一个“request

的宣传版本
let requestP = (options) => new Promise((resolve, reject) => {
    request(options, (err, res, body) => {
        if (err) {
            return reject(err);
        }
        resolve({res, body});
    });
});

其余代码可以按如下方式重写

const options = {
  url: 'https://api.imgur.com/3/gallery/hot/time/',
  headers: {
    "Authorization": "Client-ID xxxx"
  }
};

//inital request
let findImagesCatalyst = requestP(options)
.then(({res, body}) => {
    //parse the response
    body = JSON.parse(body)
        //access the data in the response
    const responseData = body.data;
    //filter only those with image counts great than 50
    const largeAlbums = responseData.filter(findDumps)
        //test to see if a dump is present
    if (largeAlbums.length > 0) {
        largeAlbums.forEach(function(i) {})
        return(largeAlbums)
    } else {
        return Promise.reject();
    }
})
//if successful in finding a dump, then go through them and find their albumIds
.then((largeAlbums) => largeAlbums.map(index => index.id))
//with the album/dump ids, get each of them with a new request
.then((dumpIds) => 
    //for each of the dumpIds create the needed url using ES6  and then request it. 
    Promise.all(dumpIds.map((i) => {
        const albumUrlOptions = {
            url: `https://api.imgur.com/3/album/${i}/images`,
            headers: {
                "Authorization": "Client-ID xxxx"
            }
        };
        return requestP(albumUrlOptions)
        .then(({res, body}) => {
            body = JSON.parse(body)
            const responseData = body.data
                //pick one sample image from the album/dump
            let sampleImage = responseData[randomSelector(responseData.length)].link;
            return({
                "dump": i,
                'sample': sampleImage
            });
        });
    }))
)
.then(function(dumps) {
    console.log(dumps)
});

答案 1 :(得分:1)

所以,你在这里有几个构建块:

  1. 请求将imgur相册反映到options对象中。
  2. findDumps - 一个简单的功能,您可以过滤相册列表。
  3. 应用前两个并返回大型相册数组的函数。这是一个异步函数,因此很可能使用Promise。
  4. 一个功能,它接收大型相册数组的每个项目并接收单个图像。这是一个异步函数,所以,再一次,一个Promise。
  5. 您希望等到收到所有单张图片
  6. 最后,您期望一组具有两个属性的对象:“dump”和“sample”。
  7. 让我们尝试构建一个例子。

    const findImagesCatalyst = new Promise((resolveImagesCatalyst, rejectImagesCatalyst) => {
      const options = {
        url: 'https://api.imgur.com/3/gallery/hot/time/',
        headers: {
          Authorization: 'Client-ID xxxx'
        }
      };
    
      request(options, (err, res, body) => {
        //access the data in the response
        const responseData = JSON.parse(body).data;
        //filter only those with image counts great than 50
        const largeAlbums = responseData.filter(findDumps);
    
        //test to see if a dump is present
        if (largeAlbums.length > 0) {
          // /!\ The trickiest part here: we won't resolve this promise until an "inner Promise" has been resolved.
          // Note that next line declares a new function to resolve inner Promise, resolveLargeAlbum. Now we have two functions:
          // - resolveImagesCatalyst - to resolve the main Promise, and
          // - resolveLargeAlbum — to resolve every image request, and there can be many of them.
          const imagesPromises = largeAlbums.map(largeAlbum => new Promise((resolveLargeAlbum, rejectLargeAlbun) => {
            // take id from every large album
            const dumpId = largeAlbum.id;
            // declare options for inner request
            const options = {
              url: `https://api.imgur.com/3/album/${i}/images`,
              headers: {
                "Authorization": "Client-ID xxxx"
              }
            };
    
            request(albumUrlOptions, (err, res, body) => {
              const responseData = JSON.parse(body).data;
                //pick one sample image from the album/dump
              const sampleImage = responseData[randomSelector(responseData.length)].link;
    
              if (sampleImage) {
                // A-HA!
                // It's inner Promise's resolve function. For N albums, there will be N resolveLargeAlbum calls. Just a few lines below, we're waiting for all of them to get resolved.
                resolveLargeAlbum({
                  dump: dumpId,
                  sample: sampleImage
                });
              } else {
                rejectLargeAlbun('Sorry, could not receive sample image:', dumpId, responseData);
              }
            });
          }));
    
          // Now we have imagePromises, an array of Promises. When you have an array of Promises, you can use Promise.all to wait until all of them are resolved:
          Promise.all(imagesPromises).then(responses => {
            // Take a look at responses: it has to be an array of objects of two properties: dump and sample.
            // Also, note that we finally use outer Promise's resolve function, resolveImagesCatalyst.
            resolveImagesCatalyst(responses);
          }).catch(errors => {
            rejectImagesCatalyst(errors);
          });
        } else {
          rejectImagesCatalyst('Sorry, nope.');
        }
      });
    });
    

    这是一个巨大的问题。你真正需要看到的是那个

    • 使用Promise.all,您可以等待Promise的集合得到解决,并且“then”部分将不会被执行,直到所有已经解决。
    • 您可以put a Promise into a Promise,并在内部承诺得到解决后解析外部承诺。
    • 代码真的很难读,因为执行顺序不是从上到下。如果您将Webpack与Babel一起使用,您可能需要查看async/await。使用async / await,代码看起来同步:你从上到下阅读它,这正是它执行的顺序结果出现,但在引擎盖下,它都是异步的。非常整洁的ES6功能,imho。

答案 2 :(得分:0)

  1. 确保没有现有的Node模块处理您的imgur搜索业务。在npms.io上搜索。

  2. 如果没有现有模块,请找一个关闭的模块并根据您的用例展开它(热图像)。

  3. 如果你真的找不到一个可以扩展的模块,那就自己动手吧。所有imgur请求都在自己的模块(和自己的文件)中。

  4. 确保该模块支持promises。

  5. 您的代码应如下所示:

  6. import {getHotAlbums, getAlbumImages, config} from 'imgur';
    
    config({clientID: 'BLAHXXXX'});
    
    async function getHotImages() {
       let hotAlbums = await getHotAlbums();
       hotAlbums = hotAlbums.filter(a => a.imageCount > 50);
    
       const sampleImages = []; 
       let albumIDs = hotAlbums.map(a => a.id);
    
       for (let albumID of albumIDs) {
         const images = await getAlbumImages(albumID);
         const randomImageNum = Math.round(Math.random()*images.length)+1;
         sampleImages.push(images[randomImageNum].link);
       }
       return sampleImages;
    }