Kaip konvertuoti esamą atgalinio ryšio API į pažadus?

Noriu dirbti su pažadais, bet turiu atgalinio ryšio API, pvz .:

1. DOM arba kito vienkartinio įvykio įkėlimas:

 window.onload; // set to callback ... window.onload = function() { }; 

2. Įprastas atgalinis atšaukimas:

 function request(onChangeHandler) { ... } request(function() { // change happened ... }); 

3. Atgalinio stiliaus mazgas („nodeback“):

 function getStuff(dat, callback) { ... } getStuff("dataParam", function(err, data) { ... }) 

4. Visa biblioteka su mazgų stiliaus atšaukimais:

 API; API.one(function(err, data) { API.two(function(err, data2) { API.three(function(err, data3) { ... }); }); }); 

Kaip galiu dirbti su API pažadais, kaip aš galiu „pažadėti“?

599
20 марта '14 в 1:47 2014-03-20 01:47 Benjaminas Gruenbaumas yra nustatytas kovo 20 d., 14 val. 1:47 2014-03-20 01:47
@ 18 atsakymų

Pažadai turi valstybę, jie pradeda laukti ir gali nuspręsti:

  • tai reiškia, kad skaičiavimas buvo sėkmingai užbaigtas.
  • atmesta vertė reiškia, kad skaičiavimas nepavyko.

Perspektyvios grįžimo funkcijos niekada neturėtų būti išmestos , o vietoj to turėtų grįžti nukrypimai. .catch grąžinimo funkcijos išskleidimas verčia naudoti tiek „ } catch { tiek „ } catch { . Žmonės, kurie naudojasi pažadėtomis API, nemano, kad žada atsisakyti. Jei nežinote, kaip asimetriniai API veikia JS, pirmiausia žr .

1. DOM arba kito vienkartinio įvykio įkėlimas:

Taigi, pažadai paprastai reiškia, kad jie nurodomi, kai jie yra nustatyti - tai reiškia, kad kai jie pereina į užbaigtą arba atmestą fazę, nurodo, kad duomenys yra prieinami (jie gali būti prieinami.).

Su šiuolaikiniais pažadų įgyvendinimais, kurie palaiko „ Promise Designer“, kaip įprasta ES6 pažadas:

 function load() { return new Promise(function(resolve, reject) { window.onload = resolve; }); } 

Tada naudokite tokį pažadą:

 load().then(function() { // Do things after onload }); 

Su bibliotekomis, palaikančiomis atidėjimą (šiam pavyzdžiui, naudokime $ q, bet vėliau naudosime ir jQuery):

 function load() { var d = $q.defer(); window.onload = function() { d.resolve(); }; return d.promise; } 

Arba naudodami jQuery kaip API, jungiantis vieną įvykį:

 function done() { var d = $.Deferred(); $("#myObject").once("click",function() { d.resolve(); }); return d.promise(); } 

2. Įprastas atgalinis atšaukimas:

Šios API yra gana įprastos, nes ... atšaukimai yra dažni JS. Apsvarstykite bendrą „ onSuccess ir „ onFail buvimo onFail :

 function getUserData(userId, onLoad, onFail) { … 

Su šiuolaikiniais pažadų įgyvendinimais, kurie palaiko „ Promise Designer“, kaip įprasta ES6 pažadas:

 function getUserDataAsync(userId) { return new Promise(function(resolve, reject) { getUserData(userId, resolve, reject); }); } 

Su bibliotekomis, kurios palaiko atidėtą (naudokitės šiuo pavyzdžiu jQuery, bet taip pat naudojome aukščiau $ q):

 function getUserDataAsync(userId) { var d = $.Deferred(); getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); }); return d.promise(); } 

jQuery taip pat siūlo formą $.Deferred(fn) , kuris turi pranašumą, leidžiantį rašyti išraišką, kuri labai tiksliai imituoja new Promise(fn) formą new Promise(fn) :

 function getUserDataAsync(userId) { return $.Deferred(function(dfrd) { getUserData(userId, dfrd.resolve, dfrd.reject); }).promise(); } 

Pastaba Čia mes naudojame faktą, kad laukiantys metodai resolve ir reject jQuery“ yra „išjungti“; tai yra. jie yra susiję su jQuery.Deferred () pavyzdžiu. Ne visos bibliotekos siūlo šią funkciją.

3. Atgalinio stiliaus mazgas („nodeback“):

Mazgų stiliaus pakvietimai turi konkretų formatą, kur atkartojimai visada yra paskutinis argumentas, o pirmasis jo parametras yra klaida. Leiskite pirmajam rankiniu būdu:

 getStuff("dataParam", function(err, data) { … 

Į:

 function getStuffAsync(param) { return new Promise(function(resolve, reject) { getStuff(param, function(err, data) { if (err !== null) reject(err); else resolve(data); }); }); } 

Atidėję galite atlikti šiuos veiksmus (šiam pavyzdžiui naudokite Q, nors Q dabar palaiko naują sintaksę, kurią norėtumėte ):

 function getStuffAsync(param) { var d = Q.defer(); getStuff(param, function(err, data) { if (err !== null) d.reject(err); else d.resolve(data); }); return d.promise; } 

Apskritai, jūs neturite per daug suprasti dalykų, dauguma pažadų bibliotekų, kurios buvo sukurtos atsižvelgiant į mazgą, ir jūsų pačių 8+ mazgo pažadai, turi įmontuotą metodą, leidžiančią pažadėti mazgų. Pavyzdžiui

 var getStuffAsync = Promise.promisify(getStuff); // Bluebird var getStuffAsync = Q.denodeify(getStuff); // Q var getStuffAsync = util.promisify(getStuff); // Native promises, node only 

4. Visa biblioteka su mazgų stiliaus atšaukimais:

Aukso taisyklė nėra, jūs pažadate juos po vieną. Tačiau kai kurie pažadų diegimo veiksmai leidžia jums tai padaryti didžiąja dalimi, pvz., „Bluebird“ programoje „nodeback“ API sąsajos konvertavimas į pažadų API yra toks pat paprastas kaip:

 Promise.promisifyAll(API); 

Arba su vietiniais pažadais mazge :

 const { promisify } = require('util'); const promiseAPI = Object.entries(API).map(v => ({key, fn: promisify(v)})) .reduce((o, p) => Object.assign(o, {[p.key]: p.fn}), {}); 

Pastabos:

  • Žinoma, kai jūs esate .then tvarkytoju, jums nereikia pažadėti dalykų. Grįžti pažadą iš prižiūrėtojo. .then nuspręskite arba atsisakykite pažado su šia verte. .then tvarkytojo mėtymas taip pat yra gera praktika ir atmes pažadą - tai garsus pažadas nutraukti saugumą.
  • Tikrosios onload atveju turėtumėte naudoti addEventListener o ne onX .
621
20 марта '14 в 1:47 2014-03-20 01:47 Atsakymą Benjaminas Gruenbaumas pateikė kovo 14 d. 14 d. 1:47 2014-03-20 01:47

Šiandien aš galiu naudoti „ PromiseNode.js kaip paprastą „Javascript“ metodą.

Paprastas ir paprastas Promise (su KISS būdu) pavyzdys:

„Simple Async Javascript API“ kodas:

 function divisionAPI (number, divider, successCallback, errorCallback) { if (divider == 0) { return errorCallback( new Error("Division by zero") ) } successCallback( number / divider ) } 

Async Promise JavaScript“ API kodas:

 function divisionAPI (number, divider) { return new Promise(function (fulfilled, rejected) { if (divider == 0) { return rejected( new Error("Division by zero") ) } fulfilled( number / divider ) }) } 

(Rekomenduoju aplankyti šį gražų šaltinį )

Be to, „ Promise galima naudoti su async\await kad ES7 async\await , async\await programos srautas tikisi fullfiled rezultato, pvz.

 function getName () { return new Promise(function (fulfilled, rejected) { var name = "John Doe"; // wait 3000 milliseconds before calling fulfilled() method setTimeout ( function() { fulfilled( name ) }, 3000 ) }) } async function foo () { var name = await getName(); // awaits for a fulfilled result! console.log(name); // the console writes "John Doe" after 3000 milliseconds } foo() // calling the foo() method to run the code 

Kiti naudojimo būdai su tuo pačiu kodu naudojant metodą .then()

 function getName () { return new Promise(function (fulfilled, rejected) { var name = "John Doe"; // wait 3000 milliseconds before calling fulfilled() method setTimeout ( function() { fulfilled( name ) }, 3000 ) }) } // the console writes "John Doe" after 3000 milliseconds getName().then(function(name){ console.log(name) }) 

Promise taip pat gali būti naudojama bet kurioje platformoje, kuri grindžiama, pvz., „Node.js“.

Premija : hibridinis metodas
(Daroma prielaida, kad atgalinio ryšio metodas turi du parametrus tiek klaidai, tiek rezultatui)

 function divisionAPI (number, divider, callback) { return new Promise(function (fulfilled, rejected) { if (divider == 0) { let error = new Error("Division by zero") callback  callback( error ) return rejected( error ) } let result = number / divider callback  callback( null, result ) fulfilled( result ) }) } 

Minėtas metodas gali reaguoti į senojo atgalinio ryšio metodo rezultatą ir žada naudoti.

Tikiuosi, kad tai padės.

37
02 янв. atsakymas duotas efkan 02 jan. 2017-01-02 16:19 '17 at 16:19 2017-01-02 16:19

Prieš konvertuojant funkciją kaip pažadą „Node.JS“

 var request = require('request'); //http wrapped module function requestWrapper(url, callback) { request.get(url, function (err, response) { if (err) { callback(err); }else{ callback(null, response); } }) } requestWrapper(url, function (err, response) { console.log(err, response) }) 

Po konvertavimo

 var request = require('request'); var Promise = require('bluebird'); function requestWrapper(url) { return new Promise(function (resolve, reject) { //returning promise request.get(url, function (err, response) { if (err) { reject(err); //promise reject }else{ resolve(response); //promise resolve } }) }) } requestWrapper('http://localhost:8080/promise_request/1').then(function(response){ console.log(response) //resolve callback(success) }).catch(function(error){ console.log(error) //reject callback(failure) }) 

Jei reikia apdoroti kelis prašymus

 var allRequests = []; allRequests.push(requestWrapper('http://localhost:8080/promise_request/1')) allRequests.push(requestWrapper('http://localhost:8080/promise_request/2')) allRequests.push(requestWrapper('http://localhost:8080/promise_request/5')) Promise.all(allRequests).then(function (results) { console.log(results);//result will be array which contains each promise response }).catch(function (err) { console.log(err) }); 
25
11 авг. atsakymas į Sivashanmugam Kannan 11 d. 2017-08-11 14:31 '17 at 14:31 pm 2017-08-11 14:31

Nemanau, kad „@Benjamin“ >

 function promiseDOMready() { return new Promise(function(resolve) { if (document.readyState === "complete") return resolve(); document.addEventListener("DOMContentLoaded", resolve); }); } promiseDOMready().then(initOnLoad); 
20
14 янв. Atsakymą Leo pateikė sausio 14 d. 2015-01-14 07:15 '15 at 7:15 2015-01-14 07:15

Node.js 8.0.0 išleidimo kandidatas turi naują įrankį, util.promisify (parašiau apie util.promisify ), kuris apima gebėjimą pažadėti bet kokią funkciją.

Tai labai skiriasi nuo kitų atsakymuose siūlomų metodų, tačiau jis turi pranašumą, nes tai yra pagrindinis metodas ir nereikalauja papildomų priklausomybių.

 const fs = require('fs'); const util = require('util'); const readFile = util.promisify(fs.readFile); 

Tada turite „ readFile metodą, kuris grąžina vietinį Promise .

 readFile('./notes.txt') .then(txt => console.log(txt)) .catch(...); 
11
16 мая '17 в 8:35 2017-05-16 08:35 atsakymą pateikė Bruno gegužės 16 d., 17 val

„Node.js 8.0.0“ apima naują util.promisify() API, leidžiančią util.promisify() standartiniu „Node.js“ atgalinio ryšio API su funkcija, kuri grąžina Promise. Toliau pateikiamas util.promisify() .

 const fs = require('fs'); const util = require('util'); const readfile = util.promisify(fs.readFile); readfile('/some/file') .then((data) => {  }) .catch((err) => {  }); 

Žr. Patobulinta pažadų parama.

6
31 мая '17 в 9:46 2017-05-31 09:46 atsakymą pateikė Gian Marco Gherardi gegužės 17 d., 17 val

Galite naudoti inline JavaScript pažadus su „Node JS“.

„My Cloud 9“ kodo nuoroda: https://ide.c9.io/adx2803/native-promises-in-node

  var express = require('express'); var request = require('request'); //Simplified HTTP request client. var app = express(); function promisify(url) { return new Promise(function (resolve, reject) { request.get(url, function (error, response, body) { if (!error  response.statusCode == 200) { resolve(body); } else { reject(error); } }) }); } //get all the albums of a user who have posted post 100 app.get('/listAlbums', function (req, res) { //get the post with post id 100 promisify('http://jsonplaceholder.typicode.com/posts/100').then(function (result) { var obj = JSON.parse(result); return promisify('http://jsonplaceholder.typicode.com/users/' + obj.userId + '/albums') }) .catch(function (e) { console.log(e); }) .then(function (result) { res.end(result); }) }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) }) //run webservice on browser : http://localhost:8081/listAlbums 
5
20 июня '16 в 16:38 2016-06-20 16:38 atsakymą pateikė „ Apoorv “ birželio 20 d. 16:38 2016-06-20 16:38

„Kriskowal Q“ bibliotekoje yra atgalinis ryšys, kad pažadėtų funkcijos. Šis metodas panašus:

 obj.prototype.dosomething(params, cb) { ...blah blah... cb(error, results); } 

galima konvertuoti naudojant Q.ninvoke

 Q.ninvoke(obj,"dosomething",params). then(function(results) { }); 
4
07 апр. Atsakyti Jason Loveman balandžio 07 d 2015-04-07 21:30 '15 - 21:30 2015-04-07 21:30

Mazge v7.6 +, kuris buvo pastatytas į pažadus ir async:

 // promisify.js let promisify = fn => (...args) => new Promise((resolve, reject) => fn(...args, (err, result) => { if (err) return reject(err); return resolve(result); }) ); module.exports = promisify; 

Kaip naudoti:

 let readdir = require('fs').readdir; let promisify = require('./promisify'); let readdirP = promisify(readdir); async function myAsyncFn(path) { let entries = await readdirP(path); return entries; } 
3
12 апр. Paul Spaulding atsakymas balandžio 12 d 2017-04-12 19:48 '17 at 7:48 pm 2017-04-12 19:48

„Node.js 8“ galite pažadėti naudoti objekto metodus skrendant šiuo moduliu:

https://www.npmjs.com/package/doasync

Jis naudoja util.promisify ir Proxy , kad jūsų objektai liktų nepakitę. Atmintinė taip pat atliekama naudojant „WeakMaps“). Štai keletas pavyzdžių:

Su objektais:

 const fs = require('fs'); const doAsync = require('doasync'); doAsync(fs).readFile('package.json', 'utf8') .then(result => { console.dir(JSON.parse(result), {colors: true}); }); 

Su funkcijomis:

 doAsync(request)('http://www.google.com') .then(({body}) => { console.log(body); // ... }); 

Jūs netgi galite naudoti vietinį call ir apply dėl tam tikro konteksto:

 doAsync(myFunc).apply(context, params) .then(result => {  }); 
2
13 окт. atsakymą pateikė Do Async spalio 13 d. 2017-10-13 01:19 '17 ne 1:19 2017-10-13 01:19

Su paprastu senu vaniliniu javaScript, čia yra sprendimas pažadėti „api“ atgalinį ryšį.

 function get(url, callback) { var xhr = new XMLHttpRequest(); xhr.open('get', url); xhr.addEventListener('readystatechange', function () { if (xhr.readyState === 4) { if (xhr.status === 200) { console.log('successful ... should call callback ... '); callback(null, JSON.parse(xhr.responseText)); } else { console.log('error ... callback with error data ... '); callback(xhr, null); } } }); xhr.send(); }  function promisify(fn) { return function () { var args = Array.prototype.slice.call(arguments); return new Promise(function(resolve, reject) { fn.apply(null, args.concat(function (err, result) { if (err) reject(err); else resolve(result); })); }); } } var get_promisified = promisify(get); var promise = get_promisified('some_url'); promise.then(function (data) { // corresponds to the resolve function console.log('successful operation: ', data); }, function (error) { console.log(error); }); 
2
28 нояб. atsakymas duotas daviddavis lapkritis 28 2016-11-28 06:07 '16 at 6:07 2016-11-28 06:07

Jei turite kelias funkcijas, atliekančias atgalinį ryšį, ir norite, kad jos grąžintų pažadą, galite naudoti šią funkciją konvertuoti.

 function callbackToPromise(func){ return function(){ // change this to use what ever promise lib you are using // In this case i'm using angular $q that I exposed on a util module var defered = util.$q.defer(); var cb = (val) => { defered.resolve(val); } var args = Array.prototype.slice.call(arguments); args.push(cb); func.apply(this, args); return defered.promise; } } 
2
04 авг. atsakymas, kurį pateikė user1852503 04 rug . 2016-08-04 03:45 '16 at 3:45 am 2016-08-04 03:45

ES6 galite naudoti integruotą pažadą , pavyzdžiui, naudoti „setTimeout“:

 enqueue(data) { const queue = this; // returns the Promise return new Promise(function (resolve, reject) { setTimeout(()=> { queue.source.push(data); resolve(queue); //call native resolve when finish } , 10); // resolve() will be called in 10 ms }); } 

Šiame pavyzdyje pažadas neturi jokios priežasties atsisakyti, todėl reject() niekada neskambinama.

1
22 янв. atsakymą pateikė Nicolas Zozol . 2017-01-22 16:22 '17 ne 4:22 2017-01-22 16:22

Atgalinio stiliaus funkcija visada yra panaši (beveik visa funkcija node.js yra stilius):

 //fs.readdir(path[, options], callback) fs.readdir('mypath',(err,files)=>console.log(files)) 

Šis stilius turi tą pačią funkciją:

  • Atgalinio ryšio funkcija perduodama paskutiniame argumente.

  • Atgalinio ryšio funkcija visada laikoma klaidos objektu kaip pirmuoju argumentu.

Taigi, galite rašyti funkciją, kuri pakeistų funkciją tokiu būdu, kaip:

 const R =require('ramda')  const checkErr = (res, rej) => (err, ...data) => R.ifElse( R.propEq('err', null), R.compose( res, R.prop('data') ), R.compose( rej, R.prop('err') ) )({err, data})  const toPromise = (fun) => (...args) => new Promise( (res, rej) => R.apply( fun, R.append( checkErr(res, rej), args ) ) ) 

Dėl glaustesnio aukščiau pateikto pavyzdžio naudojamas ramda.js. „Ramda.js“ yra puiki funkcinių programų biblioteka. Pirmiau pateiktame kode mes taikėme (pvz., Javascript function.prototype.apply ) ir pridėjome (pvz., Javascript function.prototype.push ). Taigi galime konvertuoti atgalinio stiliaus funkciją, kad pažadintume stiliaus funkciją dabar:

 const {readdir} = require('fs') const readdirP = toPromise(readdir) readdir(Path) .then( (files) => console.log(files), (err) => console.log(err) ) 

Funkcija toPromise ir checkErr priklauso berserk , tai yra funkcinių programų bibliotekų ramda.js biblioteka (sukurkite mane).

Tikiuosi, kad šis atsakymas jums bus naudingas.

1
30 июля '17 в 16:39 2017-07-30 16:39 Atsakymą pateikia JITuan LIn, liepos 30 d. 17, 16:39 2017-07-30 16:39

Ar galite padaryti kažką panašaus

 // @flow const toPromise = (f: (any) => void) => { return new Promise<any>((resolve, reject) => { try { f((result) => { resolve(result) }) } catch (e) { reject(e) } }) } export default toPromise 

Tada naudokite jį

 async loadData() { const friends = await toPromise(FriendsManager.loadFriends) console.log(friends) } 
1
09 окт. atsakymas pateikiamas onmyway133 09 okt. 2018-10-09 16:35 '18, 16:35 pm 2018-10-09 16:35

Mano perspektyvi callback funkcijos versija yra P funkcija:

https://www.npmjs.com/package/es6-promisify 

0
18 окт. atsakymas suteiktas Pujan Srivastava 18 okt. 2017-10-18 02:56 '17 at 2:56 2017-10-18 02:56

Galite naudoti „npm callback2Promise“ paketą, norėdami konvertuoti mazgų stiliaus funkcijas į pažadus.

 var c2p = require('callback2promise'); // ordinary function with any number of parameters and a callback at the end var nodeStyleFunc = function(param1, param2, callback){ setTimeout( function(){ callback(null, 'done') }, 200); } // convert the function to a promise var promise = c2p(nodeStyleFunc)(param1, param2); promise .then(result => console.log(result)) .catch(err => console.log(err)); 
-1
28 сент. Atsakymas suteikiamas „ kutlu 28“. 2016-09-28 16:45 '16 at 16:45 pm 2016-09-28 16:45

Žr. Kitus klausimus apie „ etiketes „ arba „ Užduoti klausimą“