没有 async/wait 的承诺。有人可以告诉我另一种方法来解决这个问题吗?

Promises without async/wait. Can somebody show me another way to resolve this problem?

我需要构建一个显示 2 个数组的对象。为此,我调用了第一个承诺,然后我使用它的结果调用了第二个承诺。

我想知道是否有解决此问题的最佳方法。

问题描述如下。

/**
 * DO NOT USE ASYNC/AWAIT
 * Using the below two functions produce the following output
 * {
 * authors: ['bob', 'sally'],
 * titles: ['Greeting the World', 'Hello World!']
 * }
 * */

const getBooks = () => {
    return new Promise((resolve) => {
        resolve([
        {
            bookId: 1,
            author: "bob"
        },
        {
            bookId: 2,
            author: "sally"
        }
        ]);
    });
};
  
const getTitle = (bookId) => {
    return new Promise((resolve, reject) => {
        switch (bookId) {
        case 1:
            resolve({ title: "Greeting the World" });
            break;
        case 2:
            resolve({ title: "Hello World!" });
            break;
        default:
            reject(new Error("404 - book not found"));
        }
    });
};

let authors = {authors: [], titles: []}
getBooks()
    .then(result => {                
        result.map(
            t => {
                authors.authors.push(t.author)
                getTitle(t.bookId)
                    .then(result => {
                        authors.titles.push(result.title)                         
                    })                
            })
            
            
    }).then(_ => console.log(authors))
      

    
 

您的代码仅 恰好 产生所需的结果,因为 Promise 构造函数 运行 是同步的。如果这是真的 API 并且值没有完全同步检索,您的代码将无法工作:

/**
 * DO NOT USE ASYNC/AWAIT
 * Using the below two functions produce the following output
 * {
 * authors: ['bob', 'sally'],
 * titles: ['Greeting the World', 'Hello World!']
 * }
 * */

const getBooks = () => {
    return new Promise((resolve) => {
        resolve([
        {
            bookId: 1,
            author: "bob"
        },
        {
            bookId: 2,
            author: "sally"
        }
        ]);
    });
};
  
const getTitle = (bookId) => {
    return new Promise((resolve, reject) => {
        switch (bookId) {
        case 1:
            setTimeout(() => { resolve({ title: "Greeting the World" }) });
            break;
        case 2:
            resolve({ title: "Hello World!" });
            break;
        default:
            reject(new Error("404 - book not found"));
        }
    });
};

let authors = {authors: [], titles: []}
getBooks()
    .then(result => {                
        result.map(
            t => {
                authors.authors.push(t.author)
                getTitle(t.bookId)
                    .then(result => {
                        authors.titles.push(result.title)                         
                    })                
            })
            
            
    }).then(_ => console.log(authors))

要修复它,请使用 Promise.all 等待映射的 Promise 数组解析为您需要的字符串:

const getBooks=()=>new Promise(o=>{o([{bookId:1,author:"bob"},{bookId:2,author:"sally"}])}),getTitle=o=>new Promise((e,t)=>{switch(o){case 1:e({title:"Greeting the World"});break;case 2:e({title:"Hello World!"});break;default:t(new Error("404 - book not found"))}});

getBooks()
    .then(result => Promise.all(result.map(
      obj => Promise.all([
        obj, // pass the bookId / author along to the next `.then`
        getTitle(obj.bookId) // retrieve the title for this book
      ])
    )))
    .then((resultsWithTitles) => {
      const authors = resultsWithTitles.map(([obj]) => obj.author);
      const titles = resultsWithTitles.map(([, titleObj]) => titleObj.title);
      console.log({ authors, titles });
    })
    // if this was a true API, you'd want to catch possible errors here

使用 Promise.all 获取所有书籍中的 title

let authors = { authors: [], titles: [] }
getBooks()
  .then(books => {
    const promises = books.map(book => {
      authors.authors.push(book.author)
      getTitle(book.bookId)
        .then(({ title }) => {
          authors.titles.push(title)
        });
    });
    return Promise.all(promises);
  }).then(_ => console.log(authors))

我的建议是使用 async/await 来完成类似的任务。你的代码会变成这样:

const authors = { authors: [], titles: [] }
const books = await getBooks()
for (const book of books) {
  const { title } = await getTitle(book.bookId)
  authors.authors.push(book.author)
  authors.titles.push(title)
}
console.log(authors);

承诺陷阱

看看你是如何定义 authors before 你的 Promise 序列的?这是新手做的most common mistake

这是一种方法,您可以通过对 then 调用进行排序 -

const getAuthors = () =>
  getBooks()
    .then(r => r.map(appendTitle))
    .then(r => Promise.all(r))
    .then(r => ({
      authors: r.map(b => b.author),
      titles: r.map(b => b.title)
    }))

const appendTitle = b =>
  getTitle(b.bookId)
    .then(r => Object.assign(b,r))
getAuthors ()
  .then(console.log, console.error)

{
  "authors": [
    "bob",
    "sally"
  ],
  "titles": [
    "Greeting the World",
    "Hello World!"
  ]
}

展开下面的代码片段以在您自己的浏览器中验证结果 -

const getBooks = () => {
    return new Promise((resolve) => {
        resolve([
        {
            bookId: 1,
            author: "bob"
        },
        {
            bookId: 2,
            author: "sally"
        }
        ]);
    });
};
  
const getTitle = (bookId) => {
    return new Promise((resolve, reject) => {
        switch (bookId) {
        case 1:
            resolve({ title: "Greeting the World" });
            break;
        case 2:
            resolve({ title: "Hello World!" });
            break;
        default:
            reject(new Error("404 - book not found"));
        }
    });
};

const appendTitle = b =>
  getTitle(b.bookId)
    .then(r => Object.assign(b,r))

const getAuthors = () =>
  getBooks()
    .then(r => r.map(appendTitle))
    .then(r => Promise.all(r))
    .then(r => ({
      authors: r.map(b => b.author),
      titles: r.map(b => b.title)
    }))
     
getAuthors ().then(console.log, console.error)


改进 getBooks 和 getTitle

因为 getBooksgetTitle 是简单的同步函数,我认为使用带有执行函数的 Promise 是对 Promises 的滥用。我们可以将它们重写得更简单 -

const getBooks = () =>
  Promise.resolve(BOOKS)
  
const getTitle = (bookId) =>
{ if (bookId in TITLES)
    return Promise.resolve({ title: TITLES[bookId] })
  else
    return Promise.reject(new Error("404 - book not found"))
}

其中BOOKSTITLES定义为-

const BOOKS =
  [
    {
      bookId: 1,
      author: "bob"
    },
    {
      bookId: 2,
      author: "sally"
    }
  ]

const TITLES =
  {
    1: "Greeting the World",
    2: "Hello World!"
  }

展开下面的代码片段以在您自己的浏览器中验证结果 -

const BOOKS =
  [
    {
      bookId: 1,
      author: "bob"
    },
    {
      bookId: 2,
      author: "sally"
    }
  ]

const TITLES =
  {
    1: "Greeting the World",
    2: "Hello World!"
  }

const getBooks = () =>
  Promise.resolve(BOOKS)
  
const getTitle = (bookId) =>
{ if (bookId in TITLES)
    return Promise.resolve({ title: TITLES[bookId] })
  else
    return Promise.reject(new Error("404 - book not found"))
}
const appendTitle = b =>
  getTitle(b.bookId)
    .then(r => Object.assign(b,r))

const getAuthors = () =>
  getBooks()
    .then(r => r.map(appendTitle))
    .then(r => Promise.all(r))
    .then(r => ({
      authors: r.map(b => b.author),
      titles: r.map(b => b.title)
    }))
     
getAuthors ().then(console.log, console.error)