portaldacalheta.pt
  • Κύριος
  • Επενδυτές & Χρηματοδότηση
  • Σχεδιασμός Διεπαφής Χρήστη
  • Τεχνολογία
  • Διαχείριση Έργου
Τεχνολογία

Σειριοποίηση σύνθετων αντικειμένων σε JavaScript



Απόδοση ιστότοπου και προσωρινή αποθήκευση δεδομένων

Οι σύγχρονοι ιστότοποι ανακτούν συνήθως δεδομένα από διάφορες διαφορετικές τοποθεσίες, συμπεριλαμβανομένων βάσεων δεδομένων και API τρίτων. Για παράδειγμα, κατά τον έλεγχο ταυτότητας ενός χρήστη, ένας ιστότοπος ενδέχεται να αναζητήσει την εγγραφή χρήστη από τη βάση δεδομένων και, στη συνέχεια, να τον διακοσμήσει με δεδομένα από ορισμένες εξωτερικές υπηρεσίες μέσω κλήσεων API. Η ελαχιστοποίηση των δαπανηρών κλήσεων σε αυτές τις πηγές δεδομένων, όπως η πρόσβαση στο δίσκο για ερωτήματα βάσης δεδομένων και η επιστροφή στο Διαδίκτυο για κλήσεις API, είναι απαραίτητη για τη διατήρηση ενός γρήγορου, ανταποκρινόμενου ιστότοπου. Η προσωρινή αποθήκευση δεδομένων είναι μια κοινή τεχνική βελτιστοποίησης που χρησιμοποιείται για να επιτευχθεί αυτό.

Οι διαδικασίες αποθηκεύουν τα δεδομένα εργασίας τους στη μνήμη. Εάν ένας διακομιστής ιστού εκτελείται με μία μόνο διαδικασία (όπως Node.js / Express), τότε αυτά τα δεδομένα μπορούν εύκολα να αποθηκευτούν στην κρυφή μνήμη χρησιμοποιώντας μια μνήμη cache που εκτελείται στην ίδια διαδικασία. Ωστόσο, οι διακομιστές ιστού με ισορροπία φορτίου καλύπτουν πολλές διεργασίες και ακόμη και όταν εργάζεστε με μία μόνο διαδικασία, ίσως θελήσετε να διατηρηθεί η προσωρινή μνήμη κατά την επανεκκίνηση του διακομιστή. Αυτό απαιτεί μια λύση προσωρινής αποθήκευσης εκτός της διαδικασίας, όπως το Redis, που σημαίνει ότι τα δεδομένα πρέπει να σειριοποιηθούν κατά κάποιο τρόπο και να αποστειρωθούν κατά την ανάγνωση από την προσωρινή μνήμη.



Η σειριοποίηση και η αποεστεροποίηση είναι σχετικά απλές για να επιτευχθούν σε στατικά δακτυλογραφημένες γλώσσες όπως το C #. Ωστόσο, η δυναμική φύση της JavaScript καθιστά το πρόβλημα λίγο πιο δύσκολο. Ενώ το ECMAScript 6 (ES6) εισήγαγε τάξεις, τα πεδία σε αυτές τις τάξεις (και τους τύπους τους) δεν καθορίζονται έως ότου αρχικοποιηθούν - κάτι που μπορεί να μην είναι όταν η τάξη είναι εγκατεστημένη - και οι τύποι πεδίων και λειτουργιών επιστροφής δεν καθορίζονται καθόλου στο σχήμα. Επιπλέον, η δομή της τάξης μπορεί εύκολα να αλλάξει κατά το χρόνο εκτέλεσης - τα πεδία μπορούν να προστεθούν ή να αφαιρεθούν, οι τύποι μπορούν να αλλάξουν κ.λπ. Ενώ αυτό είναι δυνατό με τη χρήση αντανάκλασης στο C #, η αντανάκλαση αντιπροσωπεύει τις «σκοτεινές τέχνες» αυτής της γλώσσας και προγραμματιστές αναμένουν ότι θα σπάσει τη λειτουργικότητα.



Μου παρουσίασαν αυτό το πρόβλημα στη δουλειά πριν από λίγα χρόνια όταν εργαζόμουν στην ομάδα πυρήνα του ApeeScape. Χτίσαμε ένα ευέλικτο ταμπλό για τις ομάδες μας, το οποίο έπρεπε να είναι γρήγορο. Διαφορετικά, οι προγραμματιστές και οι ιδιοκτήτες προϊόντων δεν θα το χρησιμοποιούσαν. Τραβήξαμε δεδομένα από διάφορες πηγές: το σύστημα παρακολούθησης εργασίας, το εργαλείο διαχείρισης έργων και μια βάση δεδομένων. Ο ιστότοπος δημιουργήθηκε στο Node.js / Express και είχαμε μια μνήμη cache για να ελαχιστοποιήσουμε τις κλήσεις σε αυτές τις πηγές δεδομένων. Ωστόσο, η ταχεία, επαναληπτική διαδικασία ανάπτυξης σήμαινε ότι αναπτύξαμε (και συνεπώς επανεκκινήσαμε) αρκετές φορές την ημέρα, ακυρώνοντας την προσωρινή μνήμη και χάνοντας έτσι πολλά από τα οφέλη της.



Μια προφανής λύση ήταν μια προσωρινή μνήμη εκτός διαδικασίας όπως Redis . Ωστόσο, μετά από κάποια έρευνα, βρήκα ότι δεν υπήρχε καλή βιβλιοθήκη σειριοποίησης για το JavaScript. Οι ενσωματωμένες μέθοδοι JSON.stringify / JSON.parse επιστρέφουν δεδομένα του τύπου αντικειμένου, χάνοντας τυχόν συναρτήσεις στα πρωτότυπα των αρχικών τάξεων. Αυτό σήμαινε ότι τα αποστειρωμένα αντικείμενα δεν μπορούσαν απλώς να χρησιμοποιηθούν «στη θέση τους» στην εφαρμογή μας, κάτι που επομένως θα απαιτούσε σημαντική αναδιαμόρφωση για να λειτουργήσει με έναν εναλλακτικό σχεδιασμό.

Απαιτήσεις για τη Βιβλιοθήκη

Προκειμένου να υποστηρίξουμε τη σειριοποίηση και την αποεστερίωση αυθαίρετων δεδομένων σε JavaScript, με τις αποστειρωμένες αναπαραστάσεις και πρωτότυπα να χρησιμοποιούνται εναλλακτικά, χρειαζόμασταν μια βιβλιοθήκη σειριοποίησης με τις ακόλουθες ιδιότητες:



  • Οι αποστειρωμένες αναπαραστάσεις πρέπει να έχουν το ίδιο πρωτότυπο (συναρτήσεις, λήψεις, ρυθμιστές) με τα αρχικά αντικείμενα.
  • Η βιβλιοθήκη πρέπει να υποστηρίζει ένθετους τύπους πολυπλοκότητας (συμπεριλαμβανομένων συστοιχιών και χαρτών), με τα πρωτότυπα των ένθετων αντικειμένων να έχουν ρυθμιστεί σωστά.
  • Θα πρέπει να είναι δυνατή η σειριοποίηση και η αποεστερίωση των ίδιων αντικειμένων πολλές φορές - η διαδικασία πρέπει να είναι αδιάφορη.
  • Η μορφή σειριοποίησης πρέπει να μεταδίδεται εύκολα μέσω TCP και να είναι αποθηκευμένη χρησιμοποιώντας το Redis ή μια παρόμοια υπηρεσία.
  • Πρέπει να απαιτούνται ελάχιστες αλλαγές κώδικα για να επισημανθεί μια κλάση ως σειριοποιήσιμη.
  • Οι ρουτίνες της βιβλιοθήκης πρέπει να είναι γρήγορες.
  • Στην ιδανική περίπτωση, θα πρέπει να υπάρχει κάποιος τρόπος για να υποστηρίξετε την αποεριοποίηση παλαιών εκδόσεων μιας τάξης, μέσω κάποιου είδους χαρτογράφησης / εκδοχής.

Εκτέλεση

Για να καλύψω αυτό το κενό, αποφάσισα να γράψω Tanagra.js , μια βιβλιοθήκη σειριοποίησης γενικής χρήσης για JavaScript. Το όνομα της βιβλιοθήκης είναι μια αναφορά σε ένα από τα αγαπημένα μου επεισόδια Star Trek: Η επόμενη γενιά , όπου το πλήρωμα του Επιχείρηση πρέπει να μάθουν να επικοινωνούν με μια μυστηριώδη εξωγήινη φυλή της οποίας η γλώσσα είναι ακατανόητη. Αυτή η βιβλιοθήκη σειριοποίησης υποστηρίζει κοινές μορφές δεδομένων για την αποφυγή τέτοιων προβλημάτων.

Tanagra.js έχει σχεδιαστεί για να είναι απλή και ελαφριά και προς το παρόν υποστηρίζει Node.js (δεν έχει δοκιμαστεί στο πρόγραμμα περιήγησης, αλλά θεωρητικά, θα πρέπει να λειτουργεί) και τάξεις ES6 (συμπεριλαμβανομένων των Χαρτών). Η κύρια εφαρμογή υποστηρίζει JSON και μια πειραματική έκδοση υποστηρίζει το Google Protocol Buffers. Η βιβλιοθήκη απαιτεί μόνο τυπική JavaScript (δοκιμάζεται επί του παρόντος με ES6 και Node.js), χωρίς εξάρτηση από πειραματικές δυνατότητες, Βαβυλωνία ανάμιξη, ή Τύπος γραφής .



Οι σειριοποιήσιμες τάξεις επισημαίνονται ως τέτοιες με μια κλήση μεθόδου κατά την εξαγωγή της τάξης:

module.exports = serializable(Foo, myUniqueSerialisationKey)



Η μέθοδος επιστρέφει a πληρεξούσιο στην τάξη, η οποία παρεμποδίζει τον κατασκευαστή και εισάγει ένα μοναδικό αναγνωριστικό. (Εάν δεν έχει καθοριστεί, αυτό είναι προεπιλεγμένο στο όνομα κλάσης.) Αυτό το κλειδί είναι σειριακό με τα υπόλοιπα δεδομένα και η κλάση το εκθέτει επίσης ως στατικό πεδίο. Εάν η κλάση περιέχει ένθετους τύπους (δηλαδή, μέλη με τύπους που χρειάζονται σειριοποίηση), καθορίζονται επίσης στη μέθοδο κλήσης:

module.exports = serializable(Foo, [Bar, Baz], myUniqueSerialisationKey)



(Οι ένθετοι τύποι για προηγούμενες εκδόσεις της τάξης μπορούν επίσης να προσδιοριστούν με παρόμοιο τρόπο, έτσι ώστε, για παράδειγμα, εάν κάνετε σειριοποίηση ενός Foo1, μπορεί να αποστειρωθεί σε ένα Foo2.)

Κατά τη διάρκεια της σειριοποίησης, η βιβλιοθήκη δημιουργεί αναδρομικά έναν παγκόσμιο χάρτη κλειδιών για τάξεις και το χρησιμοποιεί κατά την αποεπιεριοποίηση. (Θυμηθείτε, το κλειδί είναι σειριακό με τα υπόλοιπα δεδομένα.) Για να μάθετε τον τύπο της κλάσης 'ανώτερου επιπέδου', η βιβλιοθήκη απαιτεί να προσδιοριστεί αυτό στην κλήση αποεπιλογίωσης:



const foo = decodeEntity(serializedFoo, Foo)

Μια πειραματική βιβλιοθήκη αυτόματης χαρτογράφησης ακολουθεί το δέντρο ενότητας και δημιουργεί τις αντιστοιχίσεις από τα ονόματα των τάξεων, αλλά αυτό λειτουργεί μόνο για κλάσεις με μοναδικά ονόματα.

Διάταξη έργου

Το έργο χωρίζεται σε μια σειρά από ενότητες:

  • πυρήνας tanagra - κοινή λειτουργικότητα που απαιτείται από τις διάφορες μορφές σειριοποίησης, συμπεριλαμβανομένης της λειτουργίας για τη σήμανση τάξεων ως σειριοποιήσιμος
  • tanagra-json - σειριοποιεί τα δεδομένα σε μορφή JSON
  • tanagra-protobuf - σειριοποιεί τα δεδομένα σε μορφή protobuffers Google (πειραματικό)
  • tanagra-protobuf-redis-cache - μια βοηθητική βιβλιοθήκη για την αποθήκευση σειριακών πρωτοτύπων στο Redis
  • tanagra-auto-mapper - μπαίνει στο δέντρο ενότητας Node.js για να δημιουργήσετε έναν χάρτη κατηγοριών, που σημαίνει ότι ο χρήστης δεν χρειάζεται να καθορίσει τον τύπο προς αποεπιφύλαξη (πειραματικό).

Σημειώστε ότι η βιβλιοθήκη χρησιμοποιεί ορθογραφία στις ΗΠΑ.

Παράδειγμα χρήσης

Το ακόλουθο παράδειγμα δηλώνει μια σειριοποιήσιμη κλάση και χρησιμοποιεί τη μονάδα tanagra-json για την σειριοποίηση / αποεπιλογή της:

const serializable = require('tanagra-core').serializable class Foo { constructor(bar, baz1, baz2, fooBar1, fooBar2) { this.someNumber = 123 this.someString = 'hello, world!' this.bar = bar // a complex object with a prototype this.bazArray = [baz1, baz2] this.fooBarMap = new Map([ ['a', fooBar1], ['b', fooBar2] ]) } } // Mark class `Foo` as serializable and containing sub-types `Bar`, `Baz` and `FooBar` module.exports = serializable(Foo, [Bar, Baz, FooBar]) ... const json = require('tanagra-json') json.init() // or: // require('tanagra-protobuf') // await json.init() const foo = new Foo(bar, baz) const encoded = json.encodeEntity(foo) ... const decoded = json.decodeEntity(encoded, Foo)

Εκτέλεση

Συγκρίνω την απόδοση των δύο σειριοποιητών (το JSON σειριοποιητής και πειραματικός πρωτόφορα serializer) με ένα στοιχείο ελέγχου (εγγενές JSON.parse και JSON.stringify). Έκανα συνολικά 10 δοκιμές με καθεμία.

Το δοκίμασα το 2017 μου Dell XPS15 φορητός υπολογιστής με μνήμη 32Gb, που εκτελεί το Ubuntu 17.10.

Σειράωσα το ακόλουθο ένθετο αντικείμενο:

foo: { 'string': 'Hello foo', 'number': 123123, 'bars': [ { 'string': 'Complex Bar 1', 'date': '2019-01-09T18:22:25.663Z', 'baz': { 'string': 'Simple Baz', 'number': 456456, 'map': Map { 'a' => 1, 'b' => 2, 'c' => 2 } } }, { 'string': 'Complex Bar 2', 'date': '2019-01-09T18:22:25.663Z', 'baz': { 'string': 'Simple Baz', 'number': 456456, 'map': Map { 'a' => 1, 'b' => 2, 'c' => 2 } } } ], 'bazs': Map { 'baz1' => Baz { string: 'baz1', number: 111, map: Map { 'a' => 1, 'b' => 2, 'c' => 2 } }, 'baz2' => Baz { string: 'baz2', number: 222, map: Map { 'a' => 1, 'b' => 2, 'c' => 2 } }, 'baz3' => Baz { string: 'baz3', number: 333, map: Map { 'a' => 1, 'b' => 2, 'c' => 2 } } }, }

Γράψτε απόδοση

Μέθοδος σειριοποίησης Λεωφ. Inc. πρώτη δοκιμή (ms) StDev. συμπ. πρώτη δοκιμή (ms) Λ. Ex. πρώτη δοκιμή (ms) StDev. πρώην. πρώτη δοκιμή (ms)
JSON 0.115 0,0903 0,0879 0,0256
Google Protobufs 2.00 2.748 1.13 0,278
Ομάδα ελέγχου 0,0155 0,00726 0,0139 0,00570

Ανάγνωση

Μέθοδος σειριοποίησης Λεωφ. Inc. πρώτη δοκιμή (ms) StDev. συμπ. πρώτη δοκιμή (ms) Λ. Ex. πρώτη δοκιμή (ms) StDev. πρώην. πρώτη δοκιμή (ms)
JSON 0.133 0.102 0.104 0,0429
Google Protobufs 2.62 1.12 2.28 0.364
Ομάδα ελέγχου 0,0135 0,00729 0,0115 0,00390

Περίληψη

ο JSON Το serializer είναι περίπου 6-7 φορές πιο αργό από το εγγενές σειριοποίηση. Το πειραματικό πρωτόφορα Το serializer είναι περίπου 13 φορές πιο αργό από το JSON serializer ή 100 φορές πιο αργή από την εγγενή σειριοποίηση.

Επιπλέον, η εσωτερική αποθήκευση σχήματος / δομικών πληροφοριών σε κάθε σειριοποιητή έχει σαφώς επίδραση στην απόδοση. Για τον σειριοποιητή JSON, η πρώτη εγγραφή είναι περίπου τέσσερις φορές πιο αργή από τον μέσο όρο. Για το σειριακό πρόγραμμα protobuf, είναι εννέα φορές πιο αργό. Έτσι, η συγγραφή αντικειμένων των οποίων τα μεταδεδομένα έχουν ήδη αποθηκευτεί στην κρυφή μνήμη είναι πολύ πιο γρήγορη και στις δύο βιβλιοθήκες.

Το ίδιο αποτέλεσμα παρατηρήθηκε και για τις αναγνώσεις. Για τη βιβλιοθήκη JSON, η πρώτη ανάγνωση είναι περίπου τέσσερις φορές πιο αργή από τον μέσο όρο και για τη βιβλιοθήκη protobuf, είναι περίπου δυόμισι φορές πιο αργή.

Τα ζητήματα απόδοσης του σειριακού προγράμματος protobuf σημαίνουν ότι βρίσκεται ακόμα στο πειραματικό στάδιο και θα το συνιστούσα μόνο εάν χρειάζεστε τη μορφή για κάποιο λόγο. Ωστόσο, αξίζει να επενδύσετε λίγο χρόνο, καθώς η μορφή είναι πολύ πιο σφιχτή από την JSON και επομένως είναι καλύτερη για αποστολή μέσω του καλωδίου. Το Stack Exchange χρησιμοποιεί τη μορφή για την εσωτερική προσωρινή αποθήκευση.

Ο σειριοποιητής JSON είναι σαφώς πολύ πιο εκτελεστικός, αλλά εξακολουθεί να είναι πολύ πιο αργός από την εγγενή εφαρμογή. Για δέντρα μικρών αντικειμένων, αυτή η διαφορά δεν είναι σημαντική (λίγα χιλιοστά του δευτερολέπτου πάνω από ένα αίτημα 50 ms δεν θα καταστρέψει την απόδοση του ιστότοπού σας), αλλά αυτό θα μπορούσε να γίνει ζήτημα για εξαιρετικά μεγάλα δέντρα αντικειμένων και είναι μία από τις προτεραιότητες ανάπτυξης μου.

Χάρτης πορείας

Η βιβλιοθήκη βρίσκεται ακόμα στο στάδιο beta. Ο σειριοποιητής JSON είναι αρκετά καλά δοκιμασμένος και σταθερός. Εδώ είναι ο χάρτης πορείας για τους επόμενους μήνες:

  • Βελτιώσεις απόδοσης και για τους δύο σειριοποιητές
  • Καλύτερη υποστήριξη για JavaScript πριν από το ES6
  • Υποστήριξη για διακοσμητές ES-Next

Δεν γνωρίζω καμία άλλη βιβλιοθήκη JavaScript που υποστηρίζει σειριακό σύμπλεγμα, ένθετα δεδομένα αντικειμένων και αποεπιεριοποίηση στον αρχικό του τύπο. Εάν εφαρμόζετε λειτουργίες που θα ωφεληθούν από τη βιβλιοθήκη, δοκιμάστε την, επικοινωνήστε με τα σχόλιά σας και σκεφτείτε να συνεισφέρετε.

Αρχική σελίδα έργου
Αποθήκη GitHub

10 αρχές σχεδιαστικής τέχνης

Κατανόηση των βασικών

Πώς αποθηκεύονται τα αντικείμενα σε JavaScript;

Γενικά, τα αντικείμενα δεν αποθηκεύονται. Ενδείκνυται όταν χρειάζεται, χρησιμοποιούνται στην επεξεργασία και στη συνέχεια αφαιρούνται από τη μνήμη όταν δεν χρειάζονται πλέον. Αν χρειαστεί να χρησιμοποιήσουμε προσωρινά δεδομένα αλλού, κάνουμε σειριοποίηση και αποεριοποίηση αυτών των δεδομένων σε άλλη δομή.

Τι είναι ένα αντικείμενο JavaScript;

Ένα αντικείμενο είναι ένα κομμάτι κώδικα που ενσωματώνει μια δομή, μαζί με λειτουργίες που μπορούν να εκτελεστούν σε αυτήν τη δομή. Γενικά, είναι το ίδιο με ένα αντικείμενο σε οποιαδήποτε γλώσσα προγραμματισμού με αντικείμενο.

Τι είναι ένα αντικείμενο δεδομένων;

Ένα αντικείμενο δεδομένων είναι ένα κομμάτι κώδικα που περιέχει προσωρινά δεδομένα από μια αποθήκευση δεδομένων, έτσι ώστε να μπορεί να διαβαστεί ή να υποβληθεί σε επεξεργασία σε μια εφαρμογή. Εκτός αν υπάρχει τρόπος να διατηρήσετε αυτά τα δεδομένα στη μνήμη, γράφονται πίσω ή διαφορετικά δεν διατηρούνται όταν το αντικείμενο είναι εκτός εμβέλειας ή διαφορετικά δεν απαιτείται.

Γιατί χρειαζόμαστε σειριοποίηση και αποεστερίωση;

Η σειριοποίηση μας επιτρέπει να διατηρούμε δεδομένα για χρήση στη διαδικασία που εκτελείται ή σε άλλες διαδικασίες, εάν χρειάζεται. Αποθηκεύουμε τα δεδομένα και, στη συνέχεια, αποστειρώνουμε όταν χρησιμοποιούνται αλλού.

Τι συνέβη στο BlackBerry: Zombie Stock ή Comeback King;

Κερδοφορία & Αποδοτικότητα

Τι συνέβη στο BlackBerry: Zombie Stock ή Comeback King;
Η διεπαφή: Χρήση των Gatsby.js και Node.js για στατικές ενημερώσεις ιστότοπων

Η διεπαφή: Χρήση των Gatsby.js και Node.js για στατικές ενημερώσεις ιστότοπων

Τεχνολογία

Δημοφιλείς Αναρτήσεις
Scaling Scala: Τρόπος Dockerize χρησιμοποιώντας Kubernetes
Scaling Scala: Τρόπος Dockerize χρησιμοποιώντας Kubernetes
Μείωση του κόστους σε ένα ψηφιακό μέλλον πετρελαίου και φυσικού αερίου
Μείωση του κόστους σε ένα ψηφιακό μέλλον πετρελαίου και φυσικού αερίου
Το GWT Toolkit: Δημιουργήστε ισχυρές διεπαφές JavaScript χρησιμοποιώντας Java
Το GWT Toolkit: Δημιουργήστε ισχυρές διεπαφές JavaScript χρησιμοποιώντας Java
Επισκόπηση των δημοφιλών δημιουργών στατικών ιστότοπων
Επισκόπηση των δημοφιλών δημιουργών στατικών ιστότοπων
Γνωρίστε το Volt, ένα πολλά υποσχόμενο Ruby Framework για δυναμικές εφαρμογές
Γνωρίστε το Volt, ένα πολλά υποσχόμενο Ruby Framework για δυναμικές εφαρμογές
 
Οι μεγάλες ερωτήσεις οδηγούν σε εξαιρετικό σχεδιασμό - Ένας οδηγός για τη διαδικασία σκέψης σχεδιασμού
Οι μεγάλες ερωτήσεις οδηγούν σε εξαιρετικό σχεδιασμό - Ένας οδηγός για τη διαδικασία σκέψης σχεδιασμού
Η Ψυχολογία του Σχεδιασμού και η Νευροεπιστήμη του Amazing UX
Η Ψυχολογία του Σχεδιασμού και η Νευροεπιστήμη του Amazing UX
APIs στα κοινωνικά δίκτυα: Η διαδικτυακή πύλη στον πραγματικό κόσμο
APIs στα κοινωνικά δίκτυα: Η διαδικτυακή πύλη στον πραγματικό κόσμο
Οδηγός επένδυσης Family Office: Μια εναλλακτική λύση στο επιχειρηματικό κεφάλαιο
Οδηγός επένδυσης Family Office: Μια εναλλακτική λύση στο επιχειρηματικό κεφάλαιο
Αρχές Σχεδιασμού - Εισαγωγή στην Οπτική Ιεραρχία
Αρχές Σχεδιασμού - Εισαγωγή στην Οπτική Ιεραρχία
Δημοφιλείς Αναρτήσεις
  • πώς να υπολογίσετε την τιμή IPO ανά μετοχή
  • βέλτιστες πρακτικές σχεδιασμού σχεσιακών βάσεων δεδομένων
  • ψυχολογικές επιπτώσεις του μπλε χρώματος
  • ποιο πλαίσιο javascript πρέπει να χρησιμοποιήσω
  • πώς να διδάξετε τον εαυτό σας c++
Κατηγορίες
  • Επενδυτές & Χρηματοδότηση
  • Σχεδιασμός Διεπαφής Χρήστη
  • Τεχνολογία
  • Διαχείριση Έργου
  • © 2022 | Ολα Τα Δικαιώματα Διατηρούνται

    portaldacalheta.pt