Question Comment convertir une API de rappel existante en promesses?


Je veux travailler avec des promesses, mais j'ai une API de rappel dans un format comme:

1. Charge DOM ou autre événement ponctuel:

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

};

2. Callback simple:

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

3. Rappel de style de nœud ("nodeback"):

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

4. Une bibliothèque entière avec des rappels de style de noeud:

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

Comment puis-je travailler avec l'API dans les promesses, comment puis-je la «promettre»?


529
2018-03-19 22:47


origine


Réponses:


Les promesses ont un état, elles commencent comme en suspens et peuvent se régler pour:

  • rempli ce qui signifie que le calcul s'est terminé avec succès.
  • rejeté ce qui signifie que le calcul a échoué.

Promettre des fonctions de retour ne devrait jamais jeter, ils devraient renvoyer des rejets à la place. Lancer d'une fonction de retour de promesse vous forcera à utiliser à la fois un } catch {  et une .catch. Les personnes utilisant des API promisifiées ne s'attendent pas à ce que des promesses soient lancées. Si vous ne savez pas comment les API async fonctionnent dans JS - s'il vous plaît voir cette réponse premier.

1. Charge DOM ou autre événement ponctuel:

Donc, créer des promesses signifie généralement spécifier quand elles se règlent - cela signifie qu'elles passent à la phase accomplie ou rejetée pour indiquer que les données sont disponibles (et peuvent être consultées avec .then).

Avec des implémentations de promesses modernes qui soutiennent le Promise constructeur comme promesses natives ES6:

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

Vous utiliseriez alors la promesse qui en résulte comme suit:

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

Avec les bibliothèques qui supportent différé (Utilisons $ q pour cet exemple ici, mais nous utiliserons aussi jQuery plus tard):

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

Ou avec une API comme jQuery, accrocher sur un événement qui se passe une fois:

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

2. Callback simple:

Ces APIs sont plutôt courantes car les callbacks sont courants dans JS. Regardons le cas courant d'avoir onSuccess et onFail:

 function getUserData(userId, onLoad, onFail){ ...

Avec des implémentations de promesses modernes qui soutiennent le Promise constructeur comme promesses natives ES6:

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

Avec les bibliothèques qui supportent différé (Utilisons jQuery pour cet exemple ici, mais nous avons aussi utilisé $ q ci-dessus):

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

jQuery propose également un $.Deferred(fn) forme, qui a l'avantage de nous permettre d'écrire une expression qui émule de très près la new Promise(fn) forme, comme suit:

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

Note: Ici nous exploitons le fait qu'un jQuery différé resolve et reject les méthodes sont "détachables"; c'est à dire. ils sont liés à la exemple d'un jQuery.Deferred (). Toutes les bibliothèques n'offrent pas cette fonctionnalité.

3. Rappel de style de nœud ("nodeback"):

Les rappels de style de nœud (nœuds) ont un format particulier dans lequel les rappels sont toujours le dernier argument et leur premier paramètre est une erreur. Commençons par en promettre un manuellement:

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

À:

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

Avec deferreds vous pouvez faire ce qui suit (utilisons Q pour cet exemple, bien que Q supporte maintenant la nouvelle syntaxe que vous préférez):

function getStuffAsync(param){
    var d = Q.defer();
    getStuff(param,function(err,data){
         if(err !== null) return d.reject(err); // `throw err` also works here.
             d.resolve(data);
    });
    return d.promise;   
}

En général, vous ne devriez pas trop promis les choses manuellement, la plupart des bibliothèques de promesses qui ont été conçues avec Node en tête ainsi que les promesses natives dans Node 8+ ont une méthode intégrée pour promettre les nœuds de retour. Par exemple

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

4. Une bibliothèque entière avec des rappels de style de noeud:

Il n'y a pas de règle d'or ici, vous les promettez un par un. Cependant, certaines implémentations de promesses vous permettent de le faire en masse, par exemple dans Bluebird, la conversion d'une API nodeback en une API de promesse est aussi simple que:

Promise.promisifyAll(API);

Ou avec promesses natives dans Nœud:

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}), {});

Remarques:

  • Bien sûr, quand vous êtes dans un .then gestionnaire vous n'avez pas besoin de promisifier les choses. Renvoyer une promesse d'un .then gestionnaire va résoudre ou rejeter avec la valeur de cette promesse. Lancer d'un .then handler est également une bonne pratique et va rejeter la promesse - c'est la promesse de la promesse de sécurité.
  • Dans un réel onload cas, vous devez utiliser addEventListener plutôt que onX.

546
2018-03-19 22:47



Aujourd'hui, je peux utiliser Promise dans Node.js comme une méthode Javascript simple.

Un exemple simple et basique Promise (avec BAISER façon):

Plaine Code API Async Javascript:

function divisionAPI (number, divider, successCallback, errorCallback) {

    if (divider == 0) {
        return errorCallback( new Error("Division by zero") )
    }

    successCallback( number / divider )

}

Promise Code API Async Javascript:

function divisionAPI (number, divider) {

    return new Promise(function (fulfilled, rejected) {

        if (divider == 0) {
            return rejected( new Error("Division by zero") )
        }

        fulfilled( number / divider )

     })

}

(Je recommande de visiter cette belle source)

Aussi Promise peut être utilisé avec ensemble async\await dans ES7 pour faire couler le programme attendez un fullfiled résultat comme le suivant:

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

Une autre utilisation avec le même code en utilisant .then() méthode

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 peut également être utilisé sur n'importe quelle plate-forme basée sur Node.js comme react-native.

J'espère que cela t'aides.


29
2018-01-02 13:19



Je ne pense pas que window.onload La suggestion de @Benjamin fonctionnera tout le temps, car elle ne détecte pas si elle est appelée après la charge. J'ai été mordu par cela plusieurs fois. Voici une version qui devrait toujours fonctionner:

function promiseDOMready() {
    return new Promise(function(resolve) {
        if (document.readyState === "complete") return resolve();
        document.addEventListener("DOMContentLoaded", resolve);
    });
}
promiseDOMready().then(initOnLoad);

18
2018-01-14 04:15



Avant de convertir une fonction en promesse Dans 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)
})

Après la conversion

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

Incase vous devez gérer plusieurs demandes 

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

16
2017-08-11 11:31



Dans la version candidate pour Node.js 8.0.0, il y a un nouvel utilitaire, util.promisify (J'ai écrit à propos de util.promisify), qui encapsule la capacité de promettre n'importe quelle fonction.

Il n'est pas très différent des approches suggérées dans les autres réponses, mais a l'avantage d'être une méthode de base, et ne nécessite pas de dépendances supplémentaires.

const fs = require('fs');
const util = require('util');

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

Ensuite, vous avez un readFile méthode qui renvoie un natif Promise.

readFile('./notes.txt')
  .then(txt => console.log(txt))
  .catch(...);

7
2018-05-16 05:35



Vous pouvez utiliser des promesses natives JavaScript avec Node JS.

Lien de code My Cloud 9: https://ide.c9.io/adx2803/native-promises-in-node

/**
* Created by dixit-lab on 20/6/16.
*/

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
2018-06-20 13:38



Node.js 8.0.0 inclut un nouveau util.promisify() API qui permet d'encapsuler les API de style de rappel Node.js standard dans une fonction qui renvoie une promesse. Un exemple d'utilisation de util.promisify() est montré ci-dessous.

const fs = require('fs');
const util = require('util');

const readfile = util.promisify(fs.readFile);

readfile('/some/file')
  .then((data) => { /** ... **/ })
  .catch((err) => { /** ... **/ });

Voir Amélioration du support pour les promesses


4
2018-05-31 06:46



La bibliothèque Q de kriskowal inclut des fonctions de callback-to-promise. Une méthode comme celle-ci:

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

peut être converti avec Q.ninvoke

Q.ninvoke(obj,"dosomething",params).
then(function(results) {
});

3
2018-04-07 18:30



Sous le noeud v7.6 + qui a construit des promesses et 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;

Comment utiliser:

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
2018-04-12 16:48



Lorsque vous avez quelques fonctions qui effectuent un rappel et que vous voulez qu'elles renvoient une promesse, vous pouvez utiliser cette fonction pour effectuer la conversion.

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
2017-08-04 00:45