【问题标题】:What's wrong with this use of async await?这种使用异步等待有什么问题?
【发布时间】:2017-08-02 15:32:14
【问题描述】:

我正在尝试通过 soundcloud API 下载曲目,然后在下载不确定数量的曲目后启动回调。当我运行下面的代码时,我看到“全部完成”被控制台记录在其他任何事情之前,即使我打算让它成为最后一件事......我做错了什么?

// Deps
import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'

// Write mp3 function
function writeMP3(track) {
  return new Promise((resolve, reject) => {

    console.log('Starting download: ', track.title)

    request.get(track.download_url)
    .on('error', err => {
      // reject('Download error: ', err)
    })
    .on('finish', () => {
      () => resolve('Download complete')
    })
    .pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))

  })
}

async function asyncTrackFetch(track) {
  return await writeMP3(track)
}

// Array of promises to callback upon
const trackActions = []

SC.init({
  id: 'MY_ID',
  secret: 'MY_SECRET'
})

SC.get('/tracks', (err, tracks) => {

  if (err) {
    throw new Error(err)
  } else {

    console.log('Tracks fetched: ', tracks.length)

    tracks.map(track => {

      if (track.downloadable) {
        console.log('downloadable')

        trackActions.push(asyncTrackFetch(track))

      }

    })
  }
})

// Perform requests async
Promise.all(trackActions).then(() => {
  console.log('All done')
  console.log(fs.readdirSync('./data/temp'))
})

【问题讨论】:

  • SC.get('/tracks',... 是一个回调,在执行 Promise.all 之前,您无需等待它解决。您需要手动做出承诺,在 (err, tracks) => { ... } 内解决,然后在执行 Promise.all(trackActions) 之前等待
  • 啊,明白了。谢谢!不知何故,我完全忘记了 API 操作当然是异步的......
  • @MeirionHughes 知道如何编写它,因为文件写入是通过流pipe 进行的?无法理解。

标签: node.js asynchronous async-await es6-promise ecmascript-2017


【解决方案1】:

我认为最简单的方法是在tracks.map 循环完成后移动Promise.all。

更优雅的解决方案是同时承诺 SC.get 并在所有代码中使用 async await。

更新:

无法对其进行测试,因此不确定它是否有效,但它会是这样的:

import fs from 'fs'
import SC from 'node-soundcloud'
import request from 'request'

function writeMP3(track) {
  return new Promise((resolve, reject) => {

    console.log('Starting download: ', track.title)

    request.get(track.download_url)
    .on('error', err => {
      // reject('Download error: ', err)
    })
    .on('finish', () => {
      () => resolve('Download complete')
    })
    .pipe(fs.createWriteStream(`./data/temp/${track.title}_${track.user.username}.mp3`))

  })
}

function getTracks() {
  return new Promise((resolve, reject) => {

    SC.get('/tracks', (err, tracks) => {
      if (err) {
        return reject(err)
      }

      console.log('Tracks fetched: ', tracks.length)
      resolve(tracks)
    })
  })
}

SC.init({
  id: 'MY_ID',
  secret: 'MY_SECRET'
})

与async await:

async function start() {
  const tracks = await getTracks();

  for (let track of tracks) {
    await writeMP3(track)
  }
}

start()
.then(() => {
  console.log('All done')
  console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
  // insert error handler here
})

如果你只想使用 Promises:

getTracks
.then((tracks) => {
  const promiseArray = tracks.map((track) => {
    return writeMP3(track)
  })
  return Promise.all(promiseArray)
})
.then(() => {
  console.log('All done')
  console.log(fs.readdirSync('./data/temp'))
})
.catch((err) => {
  // insert error handler here
})

【讨论】:

    【解决方案2】:

    Promise.all(trackActions) 等待trackActions 中的任何承诺,但在您拨打电话时trackActions 为空。您只是在调用 SC.get 回调后向数组添加承诺。

    尝试将您的 Promise.all... 块放入 SC.get 回调中,如下所示:

    SC.get('/tracks', (err, tracks) => {
    
      if (err) {
        throw new Error(err)
      } else {
    
        console.log('Tracks fetched: ', tracks.length)
    
        tracks.map(track => {
    
          if (track.downloadable) {
            console.log('downloadable')
    
            trackActions.push(asyncTrackFetch(track))
    
          }
    
        })
    
        Promise.all(trackActions).then(() => {
          console.log('All done')
          console.log(fs.readdirSync('./data/temp'))
        })
      }
    })
    

    还值得一提的是,您的行 throw new Error(err) 将使程序崩溃,因为无法捕获该错误。

    正如 Antonio Val 所提到的,有更好的方法可以做到这一点。如果您承诺 node-soundcloud 库,那么您的代码的最后一部分可能如下所示:

    SC.get('/tracks').then(tracks => {
      // No need for trackedActions array.
      return Promise.all(tracks.filter(track => track.downloadable)
        .map(track => asyncTrackFetch(track)))
    }).then(fetchedTracks => {
      console.log('All done fetching tracks', fetchedTracks)
    }).catch(err => {
      // Handle error.
    })
    

    或在async function 内,

    try {
      const tracks = await SC.get('/tracks')
      const fetchPromises = tracks
        .filter(track => track.downloadable)
        .map(track => asyncTrackFetch(track))
      const fetchedTracks = await Promise.all(fetchPromises)
    
      console('All done fetching tracks.', fetchedTracks)
    } catch (err) {
      // Handle error
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-17
      • 2021-10-11
      • 1970-01-01
      • 2018-12-09
      • 1970-01-01
      • 2022-10-25
      • 1970-01-01
      相关资源
      最近更新 更多