de Adham El Banhawy

Cum se face o promisiune dintr-o funcție de apel invers în JavaScript

Cum se face o promisiune dintr o functie de apel invers

Dezvoltatorii back-end se confruntă permanent cu provocări în timp ce construiesc aplicații sau testează codul. Ca dezvoltator care este destul de nou și familiariza cu aceste provocări, nu am întâmpinat niciodată o provocare sau un inconvenient mai frecvent – sau mai memorabil – decât cu funcții de apel invers.

Nu o să aprofundez prea mult detaliile apelului invers și avantajele și dezavantajele acestuia sau alternative precum promisiunile și asincronizarea / așteptarea. Pentru o explicație mai vie, puteți verifica Acest articol ceea ce le explică temeinic.

Callback Hell

Rambursările sunt o caracteristică utilă a JavaScript-ului care îi permite să efectueze apeluri asincrone. Acestea sunt funcții care sunt transmise de obicei ca al doilea parametru la o altă funcție care preia date sau efectuează o operație de I / O care necesită timp pentru finalizare.

De exemplu, încercați să efectuați un apel API folosind request sau conectarea la o bază de date MongoDB. Dar dacă ambele apeluri depind unele de altele? Ce se întâmplă dacă datele pe care le preluați sunt adresa URL MongoDB la care trebuie să vă conectați?

Trebuie să cuibăriți aceste apeluri unul în celălalt:

request.get(url, function(error, response, mongoUrl) {

  if(error) throw new Error("Error while fetching fetching data");
  
  MongoClient.connect(mongoUrl, function(error, client) {
  
    if(error) throw new Error("MongoDB connection error");
    
    console.log("Connected successfully to server");    
    const db = client.db("dbName");
    // Do some application logic
    client.close();
    
  });
  
});

Bine … deci unde este problema? Ei bine, pentru un lucru, lizibilitatea codului suferă de această tehnică.

Poate părea OK la început când baza de cod este mică. Dar acest lucru nu scară bine, mai ales dacă mergeți mai mult straturi mai adânc în apelurile de apel imbricate.

Veți ajunge cu o mulțime de paranteze de închidere și acolade care vă vor încurca pe dvs. și pe alți dezvoltatori, indiferent de cât de bine formatat este codul dvs. Există un site web numit callbackhell care abordează această problemă specifică.

Îi aud pe unii dintre voi, inclusiv pe sinele meu naiv din trecut, spunându-mi că îl învelesc într-un async funcționează atunci await funcția de apel invers. Acest lucru pur și simplu nu funcționează.

Dacă există vreun bloc de cod după funcția care folosește apeluri, acel bloc de cod se va executa și nu voi aștepta pentru apelare.

Iată greșeala pe care am făcut-o înainte:

var request = require('request');

// WRONG

async function(){
    
  let joke;
  let url = "https://api.chucknorris.io/jokes/random"
  
  await request.get(url, function(error, response, data) {
      
    if(error) throw new Error("Error while fetching fetching data");
      
    let content = JSON.parse(data);
    joke = content.value;
      
  });
    
  console.log(joke); // undefined
    
};

// Wrong

async function(){
    
  let joke;
  let url = "https://api.chucknorris.io/jokes/random"
  
  request.get(url, await function(error, response, data) {
      
    if(error) throw new Error("Error while fetching fetching data");
      
    let content = JSON.parse(data);
    joke = content.value;
      
  });
    
  console.log(joke); // undefined
    
};

Unii dezvoltatori mai experimentați ar putea spune „Folosește o altă bibliotecă care folosește promisiuni pentru a face același lucru, cum ar fi axios, sau pur și simplu folosiți aduc. Sigur că pot în acest scenariu, dar asta este doar să fugi de problemă.

În plus, acesta este doar un exemplu. Uneori puteți fi blocat în utilizarea unei biblioteci care nu acceptă promisiunile fără alternative. La fel ca utilizarea kiturilor de dezvoltare software (SDK-uri) pentru a comunica cu platforme precum Amazon Web Services (AWS), Twitter sau Facebook.

Uneori, chiar și utilizarea unui apel invers pentru a efectua un apel foarte simplu cu o operație rapidă de I / O sau CRUD este în regulă și nici o altă logică nu depinde de rezultatele sale. Dar s-ar putea să fiți constrâns de mediul de rulare ca într-un Funcția Lambda care ar ucide toate procesele odată cu finalizarea firului principal, indiferent de apelurile asincrone care nu s-au finalizat.

Soluția 1 (ușoară): utilizați modulul „util” al nodului

Soluția este surprinzător de simplă. Chiar dacă sunteți puțin inconfortabil cu ideea promisiunilor în JavaScript, vă va plăcea cum puteți rezolva această problemă folosindu-le.

După cum au subliniat Erop și Robin în comentarii, versiunea Nodejs 8 și mai sus acceptă acum transformarea funcțiilor de apel invers în promisiuni folosind funcția încorporată util modul.

const request = require('request');

const util = require('util');

const url = "https://api.chucknorris.io/jokes/random";

// Use the util to promisify the request method

const getChuckNorrisFact = util.promisify(request);

// Use the new method to call the API in a modern then/catch pattern

getChuckNorrisFact(url).then(data => {

   let content = JSON.parse(data.body);
   
   console.log('Joke: ', content.value);
   
}).catch(err => console.log('error: ', err))

Codul de mai sus rezolvă corect problema folosind util.promisify metodă disponibilă din biblioteca de bază nodejs.

Tot ce trebuie să faceți este să utilizați funcția de apel invers ca argument pentru a utiliza.promisify și să o stocați ca o variabilă. În cazul meu, asta este getChuckNorrisFact.
Apoi utilizați acea variabilă ca o funcție pe care o puteți folosi ca o promisiune cu .atunci() si .captură() metode.

Soluția 2 (implicată): transformați apelul invers într-o promisiune

Uneori, utilizarea bibliotecilor de solicitare și utilizare nu este posibilă, fie că este vorba de un mediu vechi / bază de coduri sau că faceți cererile din browserul clientului, trebuie să înfășurați o promisiune în jurul funcției dvs. de apel invers.

Să luăm exemplul lui Chuck Norris de mai sus și să-l transformăm într-o promisiune.

var request = require('request');
let url = "https://api.chucknorris.io/jokes/random";

// A function that returns a promise to resolve into the data //fetched from the API or an error
let getChuckNorrisFact = (url) => {
  return new Promise(
    (resolve, reject) => {
      request.get(url, function(error, response, data){
        if (error) reject(error);
          
let content = JSON.parse(data);
        let fact = content.value;
        resolve(fact);
      })
   }
 );
};

getChuckNorrisFact(url).then(
   fact => console.log(fact) // actually outputs a string
).catch(
   error => console.(error)
);
1610985629 430 Cum se face o promisiune dintr o functie de apel invers
funcționează ca magia

În codul de mai sus, am pus callback-based request funcționează în interiorul unui ambalaj Promise Promise( (resolve, reject) => { //callback function}). Acest ambalaj ne permite să apelăm la getChuckNorrisFact funcționează ca o promisiune cu .then()și .catch() metode. Cand getChuckNorrisFact este apelat, execută cererea către API și asteapta fie pentru a resolve() sau a reject() declarație de executat. În funcția de apel invers, treceți pur și simplu datele preluate în metodele de rezolvare sau respingere.

Odată ce datele (în acest caz, un fapt minunat al lui Chuck Norris) sunt preluate și transmise către resolver, fișierul getChuckNorrisFact execută then() metodă. Acest lucru vă va întoarce rezultatul pe care îl puteți utilizați în interiorul unei funcții din interiorul then() pentru a face logica dorită – în acest caz afișarea acesteia pe consolă.

Puteți citi mai multe despre aceasta în Documente Web MDN.