Како направити обећање из функције повратног позива у ЈаваСцрипт-у

Бацк-енд програмери стално наилазе на изазове док праве апликације или тестирају код. Као програмер који је прилично нов и упознаје се са тим изазовима, никада нисам наишао на изазов или непријатност чешће - или на памтљивије - него код функција повратног позива.

Нећу улазити предубоко у детаље повратног позива и његове предности и недостатке или алтернативе попут обећања и асинхронизације / чекања. За живописније објашњење можете погледати овај чланак који их темељито објашњава.

Цаллбацк Хелл

Повратни позиви су корисна карактеристика ЈаваСцрипт-а која му омогућава упућивање асинхроних позива. То су функције које се обично преносе као други параметар другој функцији која преузима податке или ради И / О операцију за коју је потребно време.

На пример, покушајте да упутите АПИ позив помоћу requestмодул или повезивање са базом података МонгоДБ. Али шта ако оба позива зависе један од другог? Шта ако су подаци које дохваћате МонгоДБ УРЛ адреса на коју треба да се повежете?

Морали бисте угнездити ове позиве један у другом:

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(); }); });

Ок ... па у чему је проблем? Па, прво, због ове технике пати читљивост кода.

У почетку може изгледати у реду када је кодна база мала. Али ово се не прилагођава добро, посебно ако дубље улазите у угнежђене повратне позиве.

На крају ћете добити пуно завршних заграда и витичастих заграда које ће збунити вас и друге програмере без обзира на то колико је ваш код уредно форматиран. Постоји веб локација која се зове цаллбацкхелл која се бави овим специфичним проблемом.

Чујем како ми неки од вас, укључујући моју наивну прошлост, говоре да је умотам у asyncфункција, затим await функција повратног позива. Ово једноставно не иде.

Ако постоји било који блок кода након функције која користи повратне позиве, тај блок кода ће се извршити и НЕЋЕ чекати повратни позив.

Ево оне грешке коју сам раније починио:

var request = require('request'); // WRONG async function(){ let joke; let url = "//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 = "//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 };

Неки искуснији програмери могу рећи „Само користите другу библиотеку која користи обећања да би урадили исту ствар, попут аксиоса,или само користи дохватање “ . Наравно да могу у том сценарију, али то само бежи од проблема.

Осим тога, ово је само пример. Понекад можете бити закључани да користите библиотеку која не подржава обећања без алтернатива. Као да користите пакете за развој софтвера (СДК) за комуникацију са платформама као што су Амазон Веб Сервицес (АВС), Твиттер или Фацебоок.

Понекад је чак и коришћење повратног позива за обављање врло једноставног позива брзим И / О или ЦРУД операцијама у реду и ниједна друга логика не зависи од његових резултата. Али можда ће вас ограничити рунтиме окружење као у Ламбда функцији која ће убити сав процес када се заврши главна нит, без обзира на било који асинхрони позив који није завршен.

Решење 1 (лако): Користите Ноде-ов „утил“ модул

Решење је изненађујуће једноставно. Чак и ако вам је мало непријатно због идеје о обећањима у ЈаваСцрипт-у, свидеће вам се како можете да решите овај проблем користећи их.

Као што су истакли Ероп и Робин у коментарима, Нодејс верзија 8 и новија сада подржава претварање функција повратног позива у обећања помоћу уграђеног утил модула.

const request = require('request'); const util = require('util'); const url = "//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))

Горњи код уредно решава проблем помоћу функције утил.промисифиметода доступна из основне библиотеке нодејс.

Све што треба да урадите је да користите функцију повратног позива као аргумент за утил.промисифи и сачувате је као променљиву. У мом случају, то је гетЦхуцкНоррисФацт .

Тада ту променљиву користите као функцију коју можете користити попут обећања помоћу метода .тхен () и .цатцх () .

Решење 2 (укључено): Претворите повратни позив у обећање

Понекад коришћење библиотека захтева и услужних програма једноставно није могуће, било да је то због застарелог окружења / базе кода или због извршавања захтева из прегледача на страни клијента, морате да омотате обећање око функције повратног позива.

Узмимо пример Цхуцк Норрис-а горе и претворимо то у обећање.

var request = require('request'); let url = "//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) );

У горњем коду, requestфункцију засновану на повратном позиву стављам у омот Промисе Promise( (resolve, reject) => { //callback function}). Овај омотач омогућава нам да getChuckNorrisFactфункцију .then()и .catch()методе позовемо као обећање . Када се getChuckNorrisFactзове, она извршава захтев АПИ и чека за било са resolve()или reject()изјава за извршење. У функцији повратног позива једноставно пребацујете преузете податке у методе разрешавања или одбијања.

Једном када се подаци (у овом случају, сјајна чињеница о Цхуцк Норрису) преузму и проследе разрешивачу, getChuckNorrisFactизвршава then()методу. Ово ће вратити резултат који можете користити унутар функције унутар даthen() бисте извршили жељену логику - у овом случају приказивање на конзоли.

Више о томе можете прочитати у МДН веб документима.