我写了这个,但是看起来很粗糙,对于节点js来说,所以所有值都必须按顺序获取
import tripfind from '../model/tripfind';
import addbook from '../model/addbook';
import bookfind from '../model/bookfind';
function addBook(info) {
return new Promise((resolve, reject) => {
let rowcount = -1;
bookfind(0, -1, -1).then((result) => {
if (result) {
rowcount = result.rowCount;
} else {
rowcount = 0;
}
tripfind(info.trip_id, 0).then((xresult) => {
let val = '';
if (xresult === 'false') {
val = 'trip is not valid';
resolve(val);
} else {
addbook(info, rowcount).then((value) => {
if (value === 'invalid id') {
resolve('invalid id');
}
resolve(value);
}).catch((err) => {
if (err) {
reject(err);
}
});
}
}).catch((error) => {
reject(error);
});
}).catch((err) => {
if (err) {
reject(err);
}
});
});
}
export default addBook;
那是上面的代码,该代码也被导出并作为Promise函数处理,如果可以的话,请帮忙
答案 0 :(得分:1)
您遗漏了一些return
的承诺,如果您喜欢这种写作方式,则应该以相同的方式缩进then()
和catch()
。也这样做
.catch((err) => {
if (err) {
reject(err);
}
});
是没有用的,因为您遇到错误以拒绝它。以promise
模式重写(还有一些优化):
function addBook(info) {
return bookfind(0, -1, -1)
.then((result) => {
rowcount = result ? result.rowCount : 0
return tripfind(info.trip_id, 0)
.then((xresult) => {
if (xresult === 'false') {
return 'trip is not valid'
}
return addbook(info, rowcount)
})
})
}
使用async / await模式,无论如何都会更清晰:
async function addBook(info) {
let result = await bookfind(0, -1, -1)
rowcount = result ? result.rowCount : 0
let xresult = await tripfind(info.trip_id, 0)
if (xresult === 'false')
return 'trip is not valid'
let value = await addbook(info, rowcount)
return value
}
答案 1 :(得分:1)
在不使用async
/ await
的情况下,此示例在功能上与以前相同:
function addBook (info) {
return bookfind(0, -1, -1).then(result => {
const { rowCount } = result || { rowCount: 0 };
return tripfind(info.trip_id, 0).then(trip =>
trip === 'false'
? 'trip is not valid'
: addbook(info, rowCount)
);
});
}
类似
的部分.then((value) => {
if (value === 'invalid id') {
resolve('invalid id');
}
resolve(value);
})
.catch((err) => {
if (err) {
reject(err);
}
});
.catch((error) => {
reject(error);
});
是完全多余的,因此删除它们根本不会影响逻辑。删除这些内容并解开Promise
构造函数的反模式后,您可以看到逻辑变得更加简单了。
答案 2 :(得分:1)
像这样吗?
function addBook(info) {
return bookfind(0, -1, -1).then((result) => tripfind(info.trip_id, 0)
.then((xresult) => (xresult === 'false') ?
'trip is not valid' :
addbook(info, result ? result.rowCount : 0)
)
);
}
或带有async/await
async function addBook(info) {
const result = await bookfind(0, -1, -1);
const xresult = await tripfind(info.trip_id, 0)
return xresult === "false"?
'trip is not valid' :
addbook(info, result ? result.rowCount : 0);
}
避免使用Promise / Deferred反模式:What is the explicit promise construction antipattern and how do I avoid it?
并删除无用/无用的代码,例如此功能:
(value) => {
if (value === 'invalid id') {
resolve('invalid id');
}
resolve(value);
}
,它始终解析为value
。就像return value === 1? 1: value
或者所有(error) => { reject(error); }
只是将错误“转发”到返回的承诺中。
或此构造
let rowcount = -1;
//....
if (result) {
rowcount = result.rowCount;
} else {
rowcount = 0;
}
//....
doSomethingWith(rowcount)
可以总结为doSomethingWith(result ? result.rowCount : 0)
答案 3 :(得分:1)
很多人可能会告诉您async
/ await
会更好地解决这个问题。我认为问题更多是关于如何使用承诺。如果您有权更改要调用的功能,这就是我的建议。
具有函数的形状相同的事物-例如,对bookfind
的调用可以返回拒绝的承诺或包装undefined
或{..., :rowcount:12}
的已解决的承诺。如果我们删除undefined
结果并返回默认值{..., rowcount:0 }
,它将大大简化调用函数并包含更好的逻辑。
删除多余的函数调用(.catch
和addbook
.then
位)
这就是这些功能的样子(根本不知道您的设置)
function tripfind(id, num) {
return new Promise(function(resolve, reject) {
doSomething(function(err, results) {
if (err) return reject(err);
if (results === "false") return reject(new Error("trip is not valid"));
return resolve(results);
});
});
}
function bookfind(id, start, end /*or whatever*/) {
return new Promise(function(resolve, reject) {
findBooks(function(err, results) {
if (err) return reject(err);
// handle odd cases too
if (!results) return resolve({ rowcount: 0 });
return resolve(results);
});
});
}
和用法
bookfind(0, -1, -1).then(results =>{}).catch(err=>{})
tripfind(id, 0).then(results =>{}).catch(err=>{})
以这种方式使用Promise,我们不必检查results
的值,因为如果不存在String|Object|Undefined
的值,则诺言就不会解决。同样,应许产生的事物的形状应始终相同。 (当函数返回.catch
时我闻起来很香,有可能,但我会先认真研究一下)
使用此模式并删除不执行任何操作的调用(所有function addBook(info) {
return bookfind(0, -1, -1).then(({ rowcount }) =>
tripfind(info.trip_id, 0).then(() =>
addbook(info, rowcount)
)
);
}
调用),我们可以将代码简化为:
import wx
import numpy as np
import netCDF4
from netCDF4 import Dataset
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.patches as patches
import matplotlib
class Window(wx.Frame):
""" Fenêtre principale de l'application """
def __init__(self, **kwargs):
super().__init__(None, **kwargs)
RootPanel(self)
class RootPanel(wx.Panel):
""" Panel contenant tous les autres widgets de l'application """
def __init__(self, parent):
super().__init__(parent)
panel_buttons = wx.Panel(self)
panel_buttons_sizer = wx.GridSizer(1, 2, 0, 0)
self.canvas_panel = CanvasPanel(self)
self.panel_two = PanelTwo(parent=self)
select_button = PickButton(
panel_buttons,
"netCDF4 files (nc)|*.nc",
self.canvas_panel.load_from_file,
label="Show on this window (nc)",
)
toplevel_select_button = TopLevelPickButton(
panel_buttons,
"Text files (txt)|*.txt|All files|*.*",
label="Show on separate window (txt)",
)
panel_buttons_sizer.Add(select_button)
panel_buttons_sizer.Add(toplevel_select_button)
panel_buttons.SetSizer(panel_buttons_sizer)
canvas_sizer = wx.BoxSizer(wx.HORIZONTAL)
canvas_sizer.Add(self.canvas_panel,1,wx.EXPAND)
canvas_sizer.Add(self.panel_two,1,wx.EXPAND)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(panel_buttons)
sizer.Add(canvas_sizer)
self.SetSizerAndFit(sizer)
self.Show()
class PickButton(wx.Button):
""" Bouton permettant de choisir un fichier """
def __init__(self, parent, wildcard, func, **kwargs):
# func est la méthode à laquelle devra être foruni le fichier sélectionné
super().__init__(parent, **kwargs)
self.wildcard = wildcard
self.func = func
self.Bind(wx.EVT_BUTTON, self.pick_file)
def pick_file(self, evt):
style = style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE
with wx.FileDialog(
self, "Pick files", wildcard=self.wildcard, style=style
) as fileDialog:
if fileDialog.ShowModal() != wx.ID_CANCEL:
chosen_file = fileDialog.GetPath()
self.func(chosen_file)
class TopLevelPickButton(PickButton):
""" Permet de choisir un fichier et d'ouvrir une toplevel """
def __init__(self, parent, wildcard, **kwargs):
super().__init__(parent, wildcard, self.create_toplevel, **kwargs)
def create_toplevel(self, file_name):
""" Ouvre une toplevel et affiche le graphique """
self.win = TopLevelCanvas(self.Parent)
self.win.canvas_panel.load_from_file(file_name)
self.win.Show()
class CanvasPanel(wx.Panel):
""" Panel du graphique matplotlib """
def __init__(self, parent , size=(200,250)):
super().__init__(parent)
self.figure = Figure(figsize =(5,3))
self.canvas = FigureCanvas(self, -1, self.figure)
self.Size = self.canvas.Size
self.parent = parent
def load_from_file(self, file_name):
"""
Méthode effectuant l'intermédiaire pour charger le fichier selon
son type
"""
self.axes = self.figure.add_subplot(111)
if file_name.endswith(".nc"):
self._load_nc(file_name)
else:
self._load_txt(file_name)
self.canvas.draw()
def _load_txt(self, file_name):
self._load_nc(file_name)
def _load_nc(self, file_name):
""" Simule le chargement et affichage à partir d'un fichier nc """
fic='air.departure.sig995.2012.nc'
#path='/home/data/'
path=''
nc = netCDF4.Dataset(path+fic,'r')
# print("model",nc.data_model)
# print("groups",nc.groups)
# print("dimensions",nc.dimensions)
# print("variables",nc.variables)
# for dimobj in nc.dimensions.values():
# print(dimobj)
# for name in nc.ncattrs():
# print("Global attr", name, "=", getattr(nc,name))
self.lons = nc.variables['lon'][:]
self.lats = nc.variables['lat'][:]
air_dep = nc.variables['air_dep'][:,:,:]
self.lonl = len(self.lons) -1
self.latl = len(self.lats) -1
self.air_dep = air_dep[0,:,:]
self.axes.imshow(self.air_dep)
self.canvas.mpl_connect('button_press_event', self.on_press)
x = y = 1
self.rect = patches.Rectangle((x, y), 5,5,edgecolor='r', alpha=1, fill=None, label='Label')
self.axes.add_patch(self.rect)
def float_to_dms(self,f,orientation):
# convert to seconds
f = f * 3600
if f < 0:
div1 = -3600
div2 = -1
else:
div1 = 3600
div2 = 1
#Degrees,minutes,seconds
d,m = divmod(f,div1)
m,s = divmod(m*60,div1)
s, x = divmod(s*60,div1)
d,x = divmod(d,div2)
m,x = divmod(m,1)
s,x = divmod(s,1)
return (orientation+" "+str(int(d))+"° "+str(int(m)).zfill(2)+"' "+str(int(s)).zfill(2)+'"\n')
def on_press(self, click):
x1, y1 = click.xdata, click.ydata
#Longititude values are 2.5 degrees apart for this data
try:
lon = x1*2.5
except:
print("Out of bounds - Click again")
return
if lon < 180.0:
Ew = "E"
else:
Ew = "W"
lon = (lon - 360.0) -1
#Latitude values from 90 to -90 2.5 degrees apart
#split the y value
i,f = divmod(y1,1)
lat = self.lats[int(i)]
#calculate the fraction of degrees
if lat > 0 and lat < 87.5:
lat = lat+(f*2.5)
elif lat < 0 and lat > -87.5:
lat = lat-(f*2.5)
if lat < 0.0:
Ns= "S"
else:
Ns = "N"
#Convert to ° ' "
lon_dms = self.float_to_dms(lon,Ew)
lat_dms = self.float_to_dms(lat,Ns)
#air_dep seems the wrong way round but what do I know
#The way below gives values that seem correct
air = self.air_dep[int(y1),int(x1)]
air_rect =[]
x = int(x1)
y = int(y1)
# Build valid values for rectangle
# to cater for values beyond the edge of the map
if x-1 < 0: x2 = 0
else: x2 = x-2
if x+2 > self.lonl: x3 = self.lonl+1
else: x3 = x+3
if y-2 < 0:
pass
else:
air_rect.append(self.air_dep[y-2,x2:x3])
if y-1 < 0:
pass
else:
air_rect.append(self.air_dep[y-1,x2:x3])
air_rect.append(self.air_dep[y,x2:x3])
if y+1 > self.latl:
pass
else:
air_rect.append(self.air_dep[y+1,x2:x3])
if y+2 > self.latl:
pass
else:
air_rect.append(self.air_dep[y+2,x2:x3])
self.parent.panel_two.Update(x1,y1,lon,lat,air,lon_dms,lat_dms,Ns,Ew,air_rect)
zx1 = x1 - 2.5
zy1 = y1 - 2.5
zx2 = x1 + 2.5
zy2 = y1 + 2.5
self.rect.set_x(x1 - 2.5) #Move the rectangle and centre it on the X click point
self.rect.set_y(y1 - 2.5) #Move the rectangle and centre it on the Y click point
self.axes.plot()
self.canvas.draw()
class PanelTwo(wx.Panel): #here when i need to visualize pixel and coordinator cursor
def __init__(self,parent):
wx.Panel.__init__(self,parent,size=(300,250))
self.text_ctrl = wx.TextCtrl(self, -1, "", style=wx.TE_MULTILINE|wx.BORDER_SUNKEN|wx.TE_READONLY|wx.TE_RICH2, size=(300,300))
lbl = wx.StaticText(self,label="Coordinato cursor & Pixel ")
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(lbl,0, wx.ALIGN_CENTRE,10)
sizer.Add(self.text_ctrl,0, wx.ALIGN_CENTRE,10)
self.SetSizer(sizer)
def Update(self,x1,y1,lon,lat,air,lon_dms,lat_dms,Ns,Ew,air_rect):
update_str = "Mouse click at;\nX "+str(x1)+"\nLon "+Ew+" "+str(lon)+"\n"+lon_dms+"\nY "+str(y1)+"\nLat "+Ns+" "+str(lat)+"\n"+lat_dms+"\nAir Value at point "+str(air)+"\n"
self.text_ctrl.SetValue(update_str)
self.text_ctrl.write("Surrounding values\n")
#Table of surrounding temperatures
for i in range(len(air_rect)):
s = ""
line = air_rect[i]
for i2 in line:
s+="{:5.1f}".format(i2)+" "
self.text_ctrl.write(s+"\n")
class TopLevelCanvas(wx.Frame):
""" Fenêtre affichant uniquement un graph matplotlib """
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
self.canvas_panel = CanvasPanel(self)
self.zoom_panel = Zoom(parent=self)
self.Size = self.canvas_panel.Size
canvas_sizer = wx.BoxSizer(wx.HORIZONTAL)
canvas_sizer.Add(self.canvas_panel,1,wx.EXPAND)
canvas_sizer.Add(self.zoom_panel,1,wx.EXPAND)
self.SetSizerAndFit(canvas_sizer)
self.Show()
class App(wx.App):
def OnInit(self):
win = Window(title="A test dialog", size=(1000, 800))
win.Show()
return True
if __name__ == "__main__":
app = App()
app.MainLoop()