[Javascript] Covert async code to sync code with throwing Promise

const fetch = () =>
  new Promise((res) => {
    setTimeout(() => res({ user: 'zhen' }), 1150)
  })
globalThis.fetch = fetch

async function getUser() {
  return await fetch()
}

async function m1() {
  // do something
  return await getUser()
}

async function m2() {
  // do something
  return await m1()
}

async function m3() {
  // do something
  return await m2()
}

async function main() {
  const user = await m3()
  console.log('user', user)
}

 

We want to cover this async code to sync code, which means the following code should work

var fetch = () =>
  new Promise((res) => {
    setTimeout(() => res({ user: 'zhen' }), 1150)
  })
globalThis.fetch = fetch

function getUser() {
  return fetch()
}

function m1() {
  // do something
  return getUser()
}

function m2() {
  // do something
  return m1()
}

function m3() {
  // do something
  return m2()
}

function main() {
  const user = m3()
  console.log('user', user.data)
}

Of course we need to add extra code to make it works.

 

Idea:

It would be the similar way how React suspend works. When fetching some data using Promise, Suspend check, if you throw a Promise as error, it will wait the promise to be resolved or rejected, it stores all the data into a cache array... then the promise resolved or rejected... then it should rerun the original function again.

var fetch = () =>
  new Promise((res) => {
    setTimeout(() => res({ user: 'zhen' }), 1150)
  })
globalThis.fetch = fetch

function getUser() {
  return fetch()
}

function m1() {
  // do something
  return getUser()
}

function m2() {
  // do something
  return m1()
}

function m3() {
  // do something
  return m2()
}

function main() {
  const user = m3()
  console.log('user', user.data)
}

function run(func) {
  const caches = []
  let i = 0

  const originalFetch = globalThis.fetch
  fetch = function (...args) {
    if (caches[i]) {
      return caches[i]
    }
    const result = {
      status: 'pending',
      data: null,
      err: null,
    }
    caches[i++] = result

    const promise = originalFetch(...args)
      .then((data) => {
        result.status = 'fulfilled'
        result.data = data
      })
      .catch((err) => {
        result.status = 'rejected'
        result.err = err
      })

    throw promise
  }

  try {
    func()
  } catch (err) {
    if (err instanceof Promise) {
      const rerun = () => {
        i = 0
        func()
      }
      err.then(rerun, rerun)
    }
  }
}

run(main)

 

posted @ 2024-08-23 19:43  Zhentiw  阅读(6)  评论(0编辑  收藏  举报