我正在编写一个 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/