如何实现对代码的回调/等待?

时间:2018-09-28 14:21:24

标签: javascript node.js express asynchronous callback

因此,过去几天来我一直在研究此代码,并尝试实现回调/等待/任何无济于事的方法。

问题是,我该如何等待响应,直到让我说出两个函数的回调? (以及我将如何实现)

简而言之,我想做的是:

  1. 发生开机自检,getpTracks()
  2. getpTracks()开始,我们进入的每个曲目都dbChecks()
  3. 从db检查是否找到轨道,是否已将其推送到最终数组。
  4. 否则,请转到scrapeUrl()
  5. 两个函数都运行完毕后,以最终数组响应POST。

如果请求的磁道数量与最终数组中的磁道数量匹配,我每250毫秒就检查一些时髦的代码,对代码进行辅助。但这并不是我真正想要的,因为如果一次发出多个POST请求,该请求就会被破坏。

当前代码:

app.post("/spotify/playlist", (req, res) => {
  pTracks = [];
  let playlistId = req.body.id;
  t0 = performance.now();
  getpTracks(playlistId);
  let waitTime = setInterval(waitTillReady, 250);
  function waitTillReady() {
    counterLoop++;
    console.log("a: " + countera + "||" + pTracks.length);
    console.log("b: " + counterb + "||" + pTracks.length);
    console.log("a + b: " + (countera + counterb) + "||" + pTracks.length);
    console.log("Loop: " + counterLoop);
    // REPLACE WITH ASYNC OR CALLBACK!!
    if (
      countera == pTracks.length ||
      counterb == pTracks.length ||
      countera + counterb == pTracks.length ||
      counterLoop == 35 // 75 items scraping took on avg 4.8sec
    ) {
      countera = 0;
      counterb = 0;
      clearInterval(waitTime);
      res.send(pTracks);
      t1 = performance.now();
      console.log("Call took " + (t1 - t0) + " milliseconds.");
      pTracks = [];
    }
  }
});
function getpTracks(args) {
  spotifyApi.getPlaylistTracks(args, { limit: 75 }).then(function(data) {
    let temp = data.body.items;
    for (let b = 0; b < temp.length; b++) {
      let trackName = temp[b].track.name;
      for (let e = 0; e < temp[b].track.artists.length; e++) {
        var trackArtist = temp[b].track.artists[e].name;
      }
      dbChecks(trackName, trackArtist);
      //let trackId = temp[b].track.id + ",";
      //spotifyApi.getAudioFeaturesForTracks([trackId]).then(function(data) { // bpm, key etc
    }
  });
}
function dbChecks(trackName, trackArtist) {
  url = "https://www.songsterr.com/a/wa/bestMatchForQueryString?s=";
  //console.log(trackArtist + '|||' + trackName);
  //console.log(url)
  dbSongsterr
    .findOne({ artist: trackArtist, track: trackName }) // get results from mongo
    .then(response => {
      if (
        //if we find results,
        response != null &&
        response.artist == trackArtist &&
        response.track == trackName
      ) {
        countera++;
        pTracks.push({
          //push them into array
          artist: response.artist,
          name: response.track,
          url: response.url,
          tuning: response.tuning
        });
      } else if (response == null) {
        //if no results found, go webscrape
        urli = url + trackName + "&a=" + trackArtist; // url constructor
        pTracks.push({
          artist: trackArtist,
          name: trackName,
          url: urli
        });
        scrapeUrl(urli, trackName, trackArtist);
      }
    })
    .catch(error => {
      console.log("Error: " + error);
    });
}
function scrapeUrl(url, track, artist) {
  url = url;
  console.log(artist + "|||" + track);
  rp({
    url: url,
    resolveWithFullResponse: true,
    transform: function(body) {
      return cheerio.load(body);
    }
  })
    .then(async res => {
      counterb++;
      tempartist = artist.replace(/\s+/g, "");
      artistSongsterr = await res(".artist-1u304B") // tab page artist name
        .text()
        .replace(/\s+/g, "");
      //console.log(artistSongsterr);
      if (
        artistSongsterr != "" &&
        artistSongsterr.toLowerCase() == tempartist.toLowerCase()
      ) {
        // maybe add check for song aswell
        tuning = res(".tuning-1cQdvc").text(); // tab page tuning
        //console.log(tuning);
        if (tuning != "") {
          for (let y = 0; y < pTracks.length; y++) {
            if (pTracks[y].name == track && pTracks[y].tuning == null) {
              pTracks[y] = { ...pTracks[y], ...{ tuning: tuning } };
              dbSongsterr.insert({
                artist: artist,
                track: track,
                url: url,
                tuning: tuning
              });
            }
          }
        }
      } else {
        dbSongsterr.insert({
          // if didnt pass artist name check then
          artist: artist,
          track: track,
          url: false,
          tuning: false
        });
        //console.log('Artist check fail')
      }
    })
    .catch(err => {
      counterb++;
      console.log("Site crawl fail");
      pTracks.push({
        artist: track,
        name: track,
        url: false,
        tuning: false
      });
      dbSongsterr.insert({
        artist: artist,
        track: track,
        url: false,
        tuning: false
      });
    });
}

1 个答案:

答案 0 :(得分:1)

一些一般建议:

  • 您通常不需要全局变量(因此正确声明它们),如果变量应在多个函数之间共享,则函数应返回它们产生的结果。

  • 不要将.then链与await混合使用,这看起来很丑陋,令人困惑,并且可能会引入细微的错误。

这表示您的功能应该是异步的:

async function scrapeUrl(url, track, artist, result = []) {
  console.log(artist + "|||" + track);

  try {

   const res = await rp({
    url,
    resolveWithFullResponse: true,
    transform: function(body) {
      return cheerio.load(body);
    },
   });

   const tempartist = artist.replace(/\s+/g, "");
   const artistSongsterr = await res(".artist-1u304B") // tab page artist name
          .text()
          .replace(/\s+/g, "");

      if (artistSongsterr  && artistSongsterr.toLowerCase() == tempartist.toLowerCase()) {
        // maybe add check for song aswell
        const tuning = res(".tuning-1cQdvc").text(); // tab page tuning

        if (tuning) {
          for (const el of pTracks) {
            if (el.name == track && !el.tuning) {
              el.tuning = tuning;

              result.push({ url, track, artist, tuning });
            }
          }
        }
      } else {
        result.push({
          // if didnt pass artist name check then
          artist,
          track,
          url: false,
          tuning: false
        });
        //console.log('Artist check fail')
      }
    }
   } catch(error) {
      console.log("Site crawl fail");
      result.push({
        artist: track,
        name: track,
        url: false,
        tuning: false
      });

   }

   return result;
}


async function dbChecks(trackName, trackArtist, result = []) {
  const url = "https://www.songsterr.com/a/wa/bestMatchForQueryString?s=";

  try {
    const response = await dbSongsterr
      .findOne({ artist: trackArtist, track: trackName }) // get results from mongo

    if (
      //if we find results,
      response &&
      response.artist == trackArtist &&
      response.track == trackName
    ) {

      result.push({
        //push them into array
        artist: response.artist,
        name: response.track,
        url: response.url,
        tuning: response.tuning
      });
  } else {
    //if no results found, go webscrape
    const urli = url + trackName + "&a=" + trackArtist; // url constructor
    result.push({
      artist: trackArtist,
      name: trackName,
      url: urli
    });
    await scrapeUrl(urli, trackName, trackArtist, result);
  }
 } catch(error) {
  console.log("Error: " + error);
 }

 return result;
}


async function getpTracks(args) {
  const result = [];

  const data = await spotifyApi.getPlaylistTracks(args, { limit: 75 });
  let temp = data.body.items;

  for (const { track: { name: trackName, artists  }} of  temp) {

    const trackArtist = artists[artists.length - 1].name;
    // TODO: use Promise.all to parallelize
    await dbChecks(trackName, trackArtist, result);

  }

  return result;
}

可以在端点中用作:

app.post("/spotify/playlist", async (req, res) => {
  res.send(await getpTracks(req.body.id));
});