簡體   English   中英

連鎖承諾強迫異步

[英]chaining promises to force async

我正在使用諾言來獲取圖像的大相冊,並從該相冊中抽取隨機樣本。 我設法請求了所有相冊,然后將指向圖像的鏈接推送到對象數組。

現在,我想打印出該數組,但要等到我實際填寫完之后才能打印出來。 每當我在末尾添加.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) }) 

你是第二個。然后應該返回Promise。所有(承諾的)請求

.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. 要求將imgur專輯反映到options對象中。
  2. findDumps —一個簡單的函數, findDumps來過濾相冊列表。
  3. 一個應用前兩個並返回大型專輯數組的函數。 這是一個異步函數,因此可能會使用Promise。
  4. 該函數獲取大型相冊陣列中的每一項並接收單個圖像 這是一個異步函數,因此也是一個Promise。
  5. 您要等到收到所有單張圖像
  6. 最后,您期望具有兩個屬性的對象數組:“轉儲”和“樣本”。

讓我們嘗試構造一個例子。

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 ,您可以等待Promises的集合得到解決,直到所有Promise被解決,“ then”部分才會執行。
  • 您可以將Promise放入Promise中 ,並在解決內部Promise時解決外部Promise。
  • 該代碼確實很難閱讀,因為執行順序不是自上而下的。 如果您將Webpack與Babel一起使用,則可能需要看一下async / await 使用async / await,代碼看起來是同步的:您從上到下閱讀它,這恰好是執行結果的順序,但是實際上,它們都是異步的。 ES6的功能非常簡潔,恕我直言。
  1. 確保沒有現有的Node模塊可以處理您的imgur搜索業務。 在npms.io上搜索。

  2. 如果沒有現有模塊,請找到一個已關閉的模塊,然后針對您的用例進行擴展(熱圖像)。

  3. 如果您確實找不到擴展imgur的模塊,請自己制作。 所有imgur請求內容都放在其自己的模塊(和自己的文件)中。

  4. 確保該模塊支持promise。

  5. 您的代碼應如下所示:

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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM