1

Goal of the code below is to replace some links with data fetched from API.

const articleData = '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor <a data-embed=\"Instagram\" href=\"https://www.instagram.com/p/CAttGmVIIAS/">incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in <a data-embed=\"Instagram\" href=\"https://www.instagram.com/p/CAttGmVIIAS/\">reprehenderit</a> in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>'

const article = document.createElement('div')

article.innerHTML = articleData;

[...article.querySelectorAll('[data-embed=Instagram]')].forEach(link => {
    fetch(`https://api.instagram.com/oembed/?maxwidth=400&amp;url=${link.href}`)
    .then(response => {
        if (response.ok) {
            return response.json()
        }
        throw response.error
    })
    .then(json => {
        const instagramHtml = document.createElement('div')
        instagramHtml.innerHTML = json.html
        console.log(link.parentNode)
        link.parentNode.replaceChild(instagramHtml, link)
    })
})

Everything works fine until it comes to replacing. It basically happens, but only within .then method. How could I return from it?

Thank you!

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
Mitya Ustinov
  • 903
  • 3
  • 11
  • 17
  • 2
    You can't. What would be the use case for this? `.forEach()` ignores the return value of the callback. – Andreas May 29 '20 at 11:16
  • 1
    You **cannot** return it, however, if you have the ability to use something like `async/await` you could technically wrap your entire code in an `async function` and be able to work with it as if it was "synchronous". – goto May 29 '20 at 11:18
  • If you 1. create an array of fetch() requests 2. put them in Promise.all() 3. `await` the result in an `async` function, you can then access the array of results, but not "outside", only inside the async wrapper function. Still, replacing the links does work, so what else do you want to do? –  May 29 '20 at 11:22

2 Answers2

0

You cannot return a value from a Promise, however, assuming you're able to use async/await, you could do something like the following:

async function main() {
  try {
    const articleData =
      '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor <a data-embed="Instagram" href="https://www.instagram.com/p/CAttGmVIIAS/">incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in <a data-embed="Instagram" href="https://www.instagram.com/p/CAttGmVIIAS/">reprehenderit</a> in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>';
    const article = document.createElement("div");

    article.innerHTML = articleData;

    const links = [].slice.call(
      article.querySelectorAll("[data-embed=Instagram]")
    );
    // fetch all links and wait for a response
    const data = await Promise.all(links.map(link => get(link.href)));

    data.forEach(html => {
      const instagramHtml = document.createElement("div");
      instagramHtml.innerHTML = html;

      // do whatever you need to do here...
    });
  } catch (error) {
    // handle errors appropriately
    console.error(error);
  }
}
main();

function get(url) {
  return fetch(url).then(response => {
    if (response.ok) {
      return response.text();
    }
    throw response.error;
  });
}

Here's a working example:

See browser support for async/await:

goto
  • 4,336
  • 15
  • 20
0

You can do something like this:

async function doSomething(){
const articleData = '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor <a data-embed=\"Instagram\" href=\"https://www.instagram.com/p/CAttGmVIIAS/">incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in <a data-embed=\"Instagram\" href=\"https://www.instagram.com/p/CAttGmVIIAS/\">reprehenderit</a> in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>'

    const article = document.createElement('div')

    article.innerHTML = articleData;

        for (const link of [...article.querySelectorAll('[data-embed=Instagram]')]) {
        let response = await fetch(`https://api.instagram.com/oembed/?maxwidth=400&amp;url=${link.href}`);
            if (response.ok) {

            const instagramHtml = document.createElement('div')
            instagramHtml.innerHTML = response.json().html
            console.log(link.parentNode)
            link.parentNode.replaceChild(instagramHtml, link)
            }
        }

You have to forget forEach as it triggers multiple async calls without returing in sequence. Instead you will use modern for ... of loop, as mentioned in this stackoverflow question.

DevLoverUmar
  • 11,809
  • 11
  • 68
  • 98