javascript - 如何在嵌套 promise 中传播resolve()?

标签 javascript reactjs promise

我正在编写一个 React 应用程序,在特定情况下我必须解决嵌套的 Promise。该代码工作正常,但我无法将 resolve() 函数传播到外部级别,因此我无法获取返回值。

这是代码:

  writeData(data) {
    this.store.dispatch({type: "START_LOADER"})

    return new Promise((resolve, reject) => {
      this.manager.isDeviceConnected(this.deviceId).then(res => {
        this.manager.startDeviceScan(null, null, (error, device) => {
          if (device.id === this.deviceId) {

            resolve("test") // -> this is propagate correctly

            device.connect().then((device) => {
              this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
              return device.discoverAllServicesAndCharacteristics()
            }).then((device) => {
              device.writeCharacteristicWithoutResponseForService(
                data.serviceId,
                data.charId,
                data.dataToWrite
              ).then(res => {

                resolve("test2") // -> this is not propagated

              }).catch(error => {
                reject(error.message)
              })
            }).catch((error) => {
              reject(error.message)
            });
          }
        });
      }).catch(error => {
        reject(error.message)
      })
    })
  }
...
...

  async writeAsyncData(data) {
    await this.writeData(data)
  }

当我调用这个函数时:

      this.writeAsyncData({data}).then(response => {
        // here I expect whatever parameter I have passed to resolve()
        console.log(response)
      })

如果我不注释 resolve("test") ,我可以 console.log 它没有任何问题,但如果我注释它, resolve( "test2") 未显示在 console.log 中,并且 response 未定义。

如何确保即使是内部 resolve 的嵌套参数也能到达 console.log

最佳答案

为了正确嵌套 Promise,您不要将它们包装在另一个手动创建的 Promise 中。这是一种反模式。相反,你返回内心的 promise ,然后将它们束缚起来。无论最内部的 promise 返回什么,都将是整个链的解析值。

此外,当您有任何返回回调的异步操作时,您必须 promise 它们,以便您可以使用 promise 完成所有异步控制流,并且可以始终如一地进行正确的错误处理。不要将普通回调与 promise 混合在一起。控制流程,特别是正确的错误处理变得非常非常困难。从 Promise 开始,让所有异步操作都使用 Promise。

虽然此代码对于 async/await 来说可能是最简单的,但我将首先向您展示如何通过返回每个内部 promise 来正确链接所有嵌套的 promise 。

而且,为了简化嵌套代码,可以将其展平,这样您就可以将 Promise 返回到顶层并继续在那里进行处理,而不是每个级别的 Promise 都进行更深的缩进。

总结这些建议:

<强>1。不要将现有的 Promise 包装在另一个手动创建的 Promise 中。这是 Promise 反模式。除了不必要之外,正确的错误处理和错误传播很容易出错。

<强>2。 Promise 任何简单的回调。这使您可以使用 Promise 完成所有控制流程,从而更容易避免错误或您不知道如何正确传播错误的棘手情况。

<强>3。从 .then() 处理程序中返回所有内部 Promise,以将它们正确链接在一起。这允许最内部的返回值成为整个 Promise 链的解析值。它还允许错误正确地沿着链向上传播。

<强>4。压平链条。如果您有多个 Promise 链接在一起,请将它们压平,以便您始终返回到顶层,而不是创建越来越深的嵌套。您必须让事情变得更深入的一种情况是,如果您的 promise 链中有条件(这里没有)。

这是应用了这些建议的代码:

// Note: I added a timeout here so it will reject
// if this.deviceId is never found
// to avoid a situation where this promise would never resolve or reject
// This would be better if startDeviceScan() could communicate back when
// it is done with the scan
findDevice(timeout = 5000) {
    return new Promise((resolve, reject) => { 
        const timer = setTimeout(() => {
             reject(new Error("findDevice hit timeout before finding match device.id"));
        }, timeout);
        this.manager.startDeviceScan(null, null, (error, device) => { 
            if (error) {
                reject(error); 
                clearTimeout(timer);
                return
            }
            if (device.id === this.deviceId) {
                resolve(device); 
                clearTimeout(timer);
            }
        });
    });
}

writeData(data) {
    this.store.dispatch({type: "START_LOADER"});
    return this.manager.isDeviceConnected(this.deviceId).then(res => {
        return this.findDevice();
    }).then(device => {
        return device.connect();
    }).then(device => {
        this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
        return device.discoverAllServicesAndCharacteristics();
    }).then(device => {
        return device.writeCharacteristicWithoutResponseForService(
            data.serviceId,
            data.charId,
            data.dataToWrite
        );
    }).then(res => {
        return "test2";  // this will be propagated
    });
}
<小时/>

这是使用 async/await 的版本:

findDevice(timeout = 5000) {
    return new Promise((resolve, reject) => { 
        const timer = setTimeout(() => {
             reject(new Error("findDevice hit timeout before finding match device.id"));
        }, timeout);
        this.manager.startDeviceScan(null, null, (error, device) => { 
            if (error) {
                reject(error); 
                clearTimeout(timer);
                return
            }
            if (device.id === this.deviceId) {
                resolve(device); 
                clearTimeout(timer);
            }
        });
    });
}

async writeData(data) {        
    this.store.dispatch({type: "START_LOADER"});
    let res = await this.manager.isDeviceConnected(this.deviceId);
    let deviceA = await this.findDevice();
    let device = await deviceA.connect();
    this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
    await device.discoverAllServicesAndCharacteristics();
    let res = await device.writeCharacteristicWithoutResponseForService(
          data.serviceId,
          data.charId,
          data.dataToWrite
    );
    return "something";    // final resolved value 
}

注意:在原始代码中,device 有两个重写定义。我将其保留在代码的第一个版本中,但在第二个版本中将第一个版本更改为 deviceA

注意:在编写代码时,如果 this.manager.startDeviceScan() 从未找到匹配的设备,其中 device.id === this. deviceId,您的代码将被卡住,永远不会解析或拒绝。这似乎是一个很难发现的错误等待发生。在绝对最坏的情况下,它应该有一个超时,如果从未找到,则会拒绝,但可能 startDeviceScan 的实现需要在扫描完成时进行通信,以便外部代码可以在没有匹配设备时拒绝找到了。

注意:我发现您从未使用 this.manager.isDeviceConnected(this.deviceId); 中解析的值。这是为什么?如果设备未连接,是否会拒绝。如果没有,这似乎是一个无操作(没有做任何有用的事情)。

注意:您调用并等待device.discoverAllServicesAndCharacteristics();,但您从未使用它的任何结果。这是为什么?

关于javascript - 如何在嵌套 promise 中传播resolve()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58903442/

相关文章:

javascript - 显示前一天的 Google Apps 脚本

javascript - 使用 javascript 的条件必填字段 - SuiteCRM 7.3.1

javascript - 为html表格中的每一行添加倒计时

d3.js v5 - Promise.all 替换 d3.queue

javascript - AXIOS:如何并发运行http请求并在请求失败时获取所有请求事件的结果

Javascript - 查询 parse.com 的匹配项目

javascript - Response.Redirect(Request.RawUrl) 之后的 Response.Write

javascript - 为什么 props 在 render 和 componentWillReceiveProps 中显示不同的值?

javascript - 如何在 React Native 中的水平 ScrollView 中放置粘性 Touchable?

reactjs - react 教程 : CommentForm's onSubmit not working