如何“嵌套”javascript承诺?

时间:2021-07-17 12:18:18

标签: javascript promise fetch

我编写了一个函数来使用 fetch 捕获非 200 个结果:

 1 function $get(url, callback) {
 2  fetch(url, {credentials: "same-origin"})
 3    .then(resp => {
 4      if (!resp.ok) {
 5        resp.text().then((mesg) => {
 6          throw {"stat": resp.status, "mesg": mesg.trim()}
 7        })
 8        return resp.text()
 9      } 
10      return resp.json() 
11    })
12    .then(data => callback({"stat": 200, "data": data}))
13    .catch(error => callback(error))
14}

我在第 9 行出错:

ERROR: TypeError: Failed to execute 'text' on 'Response': body stream already read

我必须写第5~7行代码的原因是如果我写:

if (!resp.ok) {
  throw {"stat": resp.status, "mesg": resp.statusText}
return resp.json()

我会收到类似 {"stat": 403, "mesg": "Forbidden"} 的错误消息,而我想要的是: {"stat": 403, "mesg": "invalid user name or password"}

在服务器端,我的 go 程序会像这样生成非 200 回复:

> GET /api/login?u=asdf&p=asdf HTTP/1.1
> Host: localhost:7887
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Sat, 17 Jul 2021 11:53:16 GMT
< Content-Length: 25
< 
invalid username or password

即go 库不会修改 http 状态文本,而是将错误消息放在 body 中,这可能是 http 标准规定的(例如,状态文本不能更改)。

所以,我的问题是:

  • 如何在不使用 promise 的情况下读取非 200 回复的正文?
  • 或者,如果出现错误,如何回复“空”承诺,以防止再次读取流?

=== 编辑 ===

以下代码可以正常工作,但是,正如注释所指出的那样,它似乎使用了“反”模式:

function $get(url, callback) {
  fetch(url, {credentials: "same-origin"})
    .then(resp => {
      if (!resp.ok) {
        resp.text().then((mesg) => {
          callback({"stat": resp.status, "mesg": mesg.trim()})
        })
        return new Promise(function(_, _) {}) 
      } 
      return resp.json()
    })
    .then(data => callback({"stat": 200, "data": data}))
    .catch(error => { console.log(`GET ${url}\nERROR: ${error}`) })
}

但是,这行不通

function $get(url, callback) {
  fetch(url, {credentials: "same-origin"})
    .then(resp => {
      if (!resp.ok) {
        resp.text().then((mesg) => {
          throw `{"stat": resp.status, "mesg": mesg.trim()}`
        }) 
      } 
      return resp.json()
    })
    .then(data => callback({"stat": 200, "data": data}))
    .catch(error => { console.log(`GET ${url}\nERROR: ${error}`) })
}

throw 将生成此错误,而不是将控制权传递给下面的 catch

127.0.0.1/:1 Uncaught (in promise) {"stat": resp.status, "mesg": mesg.trim()}

2 个答案:

答案 0 :(得分:3)

考虑到您使用的是 fetch,您还可以使用 async/await 并执行以下操作。 :

async function $get(url, callback) {
  try {
    const resp = await fetch(url, {credentials: "same-origin"});
    
    if (!resp.ok) {
      // this will end up in the catch statement below
      throw({ stat: resp.status, mesg: (await resp.text()).trim());
    }
    
    callback({ stat: 200, data: await resp.json() });
  } catch(error) {
    callback(error);
  }
}

我不明白你为什么要使用 callback 函数 :) 那些都是 1999 年


为了解释您的错误,您在出现错误时调用了 resp.text() 两次。为了防止这种情况,您应该立即返回从第一个 resp.text() 调用链接的承诺。这也会抛出错误并在没有到达连续的 then() 语句的情况下在 catch 块中结束:

function $get(url, callback) {
 fetch(url, {credentials: "same-origin"})
   .then(resp => {
     if (!resp.ok) {
       return resp.text().then((mesg) => {
//     ^^^^^^
         throw {stat: resp.status, error: mesg.trim()}
       });
     } 
     return resp.json() ;
   })
   .then(data => callback({stat: 200, data }))
   .catch(error => callback(error))
}

不使用回调的“正确”$get 函数:

function $get(url) {
  return fetch(url, {credentials: "same-origin"}).then(async (resp) => {
    const stat = resp.status;

    if (!resp.ok) {
      throw({ stat, error: (await resp.text()).trim() });
    }

    return { stat, data: await resp.json() };
  });
}

你可以像这样消费:

$get('https://example.com')
  .then(({ stat, data }) => {

  })
  .catch({ stat, error}) => {

  })

答案 1 :(得分:1)

只需返回被拒绝的承诺即可

 1 function $get(url, callback) {
 2  fetch(url, {credentials: "same-origin"})
 3    .then(resp => {
 4      if (!resp.ok) {
 5        return resp.text().then((mesg) => {
 6          throw {"stat": resp.status, "mesg": mesg.trim()}
 7        })
 9      } 
10      return resp.json() 
11    })
12    .then(data => callback({"stat": 200, "data": data}))
13    .catch(error => callback(error))
14}