javascript - utiliser - google map api example




Comment convertir une API de rappel existante en promesses? (12)

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»?


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

Un exemple simple et basique pour Promise (avec KISS way):

Code API Async Javascript simple :

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

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

    successCallback( number / divider )

}

Promise JavaScript Async API code:

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 )

Promise peut également être utilisé avec async\await dans ES7 pour que le flux du programme attende un résultat fullfiled comme ceci:

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 la méthode .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 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.


Avec l'ancien javaScript vanilla, voici une solution pour promettre un callback api.

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: convert api based callbacks to promises
     * @description takes in a factory function and promisifies it
     * @params {function} input function to promisify
     * @params {array} an array of inputs to the function to be promisified
     * @return {function} promisified function
     * */
    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);
});

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 une méthode readFile qui renvoie une Promise native.

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

Je ne pense pas que la suggestion window.onload par @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);

La fonction de rappel fonctionne toujours comme ceci (presque toutes les fonctions dans node.js sont ce style):

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

Ce style a la même caractéristique:

  1. la fonction de rappel est passée par le dernier argument.

  2. la fonction de rappel accepte toujours l'objet d'erreur comme premier argument.

Ainsi, vous pourriez écrire une fonction pour convertir une fonction avec ce style comme ceci:

const R =require('ramda')

/**
 * A convenient function for handle error in callback function.
 * Accept two function res(resolve) and rej(reject) ,
 * return a wrap function that accept a list arguments,
 * the first argument as error, if error is null,
 * the res function will call,else the rej function.
 * @param {function} res the function which will call when no error throw
 * @param {function} rej the function which will call when  error occur
 * @return {function} return a function that accept a list arguments,
 * the first argument as error, if error is null, the res function
 * will call,else the rej function
 **/
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})

/**
 * wrap the callback style function to Promise style function,
 * the callback style function must restrict by convention:
 * 1. the function must put the callback function where the last of arguments,
 * such as (arg1,arg2,arg3,arg...,callback)
 * 2. the callback function must call as callback(err,arg1,arg2,arg...)
 * @param {function} fun the callback style function to transform
 * @return {function} return the new function that will return a Promise,
 * while the origin function throw a error, the Promise will be Promise.reject(error),
 * while the origin function work fine, the Promise will be Promise.resolve(args: array),
 * the args is which callback function accept
 * */
 const toPromise = (fun) => (...args) => new Promise(
    (res, rej) => R.apply(
        fun,
        R.append(
            checkErr(res, rej),
            args
        )
    )
)

Pour plus concis, ci-dessus exemple utilisé ramda.js. Ramda.js est une excellente bibliothèque pour la programmation fonctionnelle. Dans le code ci-dessus, nous l'avons utilisé (comme javascript function.prototype.apply ) et ajouté (comme javascript function.prototype.push ). Ainsi, nous pourrions convertir la fonction de style de rappel en promesse de fonction de style maintenant:

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

La fonction toPromise et checkErr est propre à berserk library, c'est une bibliothèque de programmation fonctionnelle fork par ramda.js (crée par moi).

J'espère que cette réponse vous sera utile.


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é.

Les fonctions de promesses de renvoi ne devraient jamais être lancées , elles devraient renvoyer les rejets à la place. Lancer à partir d'une fonction de retour de promesse vous forcera à utiliser à la fois un } catch { et un .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, veuillez d'abord consulter cette réponse .

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

Ainsi, 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 accédées avec .then ).

Avec des implémentations de promesses modernes qui prennent en charge le constructeur Promise comme les 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 prennent en charge le constructeur Promise comme les 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) , qui a l'avantage de nous permettre d'écrire une expression qui émule de très près la new Promise(fn) , comme suit:

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

Note: Ici, nous exploitons le fait que les méthodes de resolve et de reject jQuery différées sont «détachables»; c'est à dire. ils sont liés à l' instance 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 des promesses natives dans Node:

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

Remarques:

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

Ma version de promisify d'une fonction de callback est la fonction P :

var P = function() {
  var self = this;
  var method = arguments[0];
  var params = Array.prototype.slice.call(arguments, 1);
  return new Promise((resolve, reject) => {
    if (method && typeof(method) == 'function') {
      params.push(function(err, state) {
        if (!err) return resolve(state)
        else return reject(err);
      });
      method.apply(self, params);
    } else return reject(new Error('not a function'));
  });
}
var callback = function(par, callback) {
  var rnd = Math.floor(Math.random() * 2) + 1;
  return rnd > 1 ? callback(null, par) : callback(new Error("trap"));
}

callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))
callback("callback", (err, state) => err ? console.error(err) : console.log(state))

P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))
P(callback, "promise").then(v => console.log(v)).catch(e => console.error(e))

La fonction P nécessite que la signature de callback(error,result) soit callback(error,result) .


Node.js 8.0.0 inclut une nouvelle API util.promisify() qui permet d' util.promisify() API de style de rappel Node.js standard dans une fonction qui renvoie une promesse. Un exemple d'utilisation de util.promisify() est présenté 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


Vous pouvez utiliser Promise natif dans ES6, par exemple avec 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
    });

}

Dans cet exemple, la Promesse n'a aucune raison d'échouer, donc reject() n'est jamais appelé.


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

es6-promisify convertit les fonctions basées sur le rappel en fonctions Promise.

const promisify = require('es6-promisify');

const promisedFn = promisify(callbackedFn, args);

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


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


requestWrapper(url,function(response){
    console.log(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)
});






bluebird