如何"nest"javascript承诺?

How to "nest" javascript promise?

我用 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 标准强制要求的(例如,状态文本不能更改)。

所以,我的问题是:

=== 编辑 ===

下面的代码工作正常,但是,它似乎使用了评论指出的“反”模式:

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()}

考虑到您正在使用 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()。为防止这种情况,您应该立即 return 从第一个 resp.text() 调用链接的承诺。这也将抛出错误并最终进入 catch 块,而没有到达连续的 then() 语句:

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}) => {

  })

只是 return 被拒绝的承诺,你就可以开始了

 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}