Home  >  Q&A  >  body text

Rewrite the title as: How to convert existing callback API to Promise form?

<p>I want to use promises to handle it, but the format of my callback API is as follows: </p> <h3>1. DOM loading or other one-time events: </h3> <pre class="brush:php;toolbar:false;">window.onload; // Set as callback function ... window.onload = function() { };</pre> <h3>2. Ordinary callback function: </h3> <pre class="brush:php;toolbar:false;">function request(onChangeHandler) { ... } request(function() { // changed ... });</pre> <h3>3. Node-style callback function ("nodeback"): </h3> <pre class="brush:php;toolbar:false;">function getStuff(dat, callback) { ... } getStuff("dataParam", function(err, data) { ... })</pre> <h3>4. The entire library uses Node-style callback functions: </h3> <pre class="brush:php;toolbar:false;">API; API.one(function(err, data) { API.two(function(err, data2) { API.three(function(err, data3) { ... }); }); });</pre> <h3>How do I use promises to handle this API and how do I "promisify" it? </h3>
P粉268654873P粉268654873425 days ago375

reply all(2)I'll reply

  • P粉618358260

    P粉6183582602023-08-22 16:14:21

    Today, I can use Promise as a normal Javascript method in Node.js.

    A simple and basic Promise example (using the KISS method):

    NormalJavascript asynchronous API code:

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

    Promise Javascript asynchronous 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 )
    
         })
    
    }

    (I recommend visiting this excellent source )

    In addition, Promise can also be used with async\await in ES7 to make the program flow wait for the fulfilled result, as follows Shown:

    function getName () {
    
        return new Promise(function (fulfilled, rejected) {
    
            var name = "John Doe";
    
            // 在调用fulfilled()方法之前等待3000毫秒
            setTimeout ( 
                function() {
                    fulfilled( name )
                }, 
                3000
            )
    
        })
    
    }
    
    
    async function foo () {
    
        var name = await getName(); // 等待fulfilled结果!
    
        console.log(name); // 控制台在3000毫秒后输出"John Doe"
    
    }
    
    
    foo() // 调用foo()方法运行代码

    Using the same code, you can use the .then() method:

    function getName () {
    
        return new Promise(function (fulfilled, rejected) {
    
            var name = "John Doe";
    
            // 在调用fulfilled()方法之前等待3000毫秒
            setTimeout ( 
                function() {
                    fulfilled( name )
                }, 
                3000
            )
    
        })
    
    }
    
    
    // 控制台在3000毫秒后输出"John Doe"
    getName().then(function(name){ console.log(name) })

    Promise can also be used on any Node.js-based platform, such as react-native.

    Bonus: A hybridapproach
    (Assume that the callback method has two parameters, namely error and result)

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

    The above method can respond to the results of old-style callbacks and Promise usage at the same time.

    Hope this helps.

    reply
    0
  • P粉680087550

    P粉6800875502023-08-22 11:07:09

    Promises have a state, they start out in a pending state and can be resolved as:

    • ImplementedIndicates that the calculation was completed successfully.
    • Rejected means the calculation failed.

    Functions that return a promise should not throw an exception but should return a rejection. Throwing an exception from a function that returns a promise will force you to use both } catch { and .catch. People using promise-based APIs don't want promises to throw exceptions. If you're not sure how the async API in JS works, first check out this answer.

    1. DOM loading or other one-time events:

    Therefore, creating promises usually means specifying when they resolve - meaning when they move to the Fulfilled or Rejected stage to indicate that the data is available (and accessible using .then).

    Use a modern promise implementation that supports Promise constructors (such as native ES6 promises):

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

    You can then use the generated promise like this:

    load().then(function() {
        // 在onload之后执行操作
    });

    Use a library that supports delay (here we use $q as an example, but we will also use jQuery later):

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

    Or use an API similar to jQuery to hook an event that occurs:

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

    2. Ordinary callback:

    These APIs are quite common because callbacks are common in JS. Let's look at onSuccess and onFail in common cases:

    function getUserData(userId, onLoad, onFail) { …

    Use a modern promise implementation that supports Promise constructors (such as native ES6 promises):

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

    Use a library that supports delay (here we're using jQuery as an example, but we also used $q before):

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

    jQuery also provides the $.Deferred(fn) form, which has the advantage of allowing us to write an expression very close to the new Promise(fn) form, as shown below :

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

    Note: Here we take advantage of the fact that jQuery's deferred resolve and reject methods are "detachable"; that is, they are bound to jQuery.Deferred()'s Example. Not all libraries provide this functionality.

    3. Node style callback ("nodeback"):

    Node style callbacks (nodebacks) have a specific format where the callback is always the last parameter and its first parameter is the error. First manually convert it to a promise:

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

    Convert to:

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

    Using defer, you can do the following (we used Q as an example, although Q now supports a new syntax You should prefer that syntax ):

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

    In general you shouldn't manually convert things to promises too much, most promise libraries designed for Node as well as native promises in Node 8 have built-in methods for converting nodebacks to promises. For example

    var getStuffAsync = Promise.promisify(getStuff); // Bluebird
    var getStuffAsync = Q.denodeify(getStuff); // Q
    var getStuffAsync = util.promisify(getStuff); // 原生承诺,仅限Node

    4. The entire library uses Node style callbacks:

    There is no golden rule here, you can convert them into promises one by one. However, some promise implementations allow you to do this in batches, for example in Bluebird converting the nodeback API to a promise API is as simple as this:

    Promise.promisifyAll(API);

    Or use Native Promise in Node:

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

    Notice:

    • Of course you don't need to convert things into promises when you are inside a .then handler. Returning a promise from the .then handler will be resolved or rejected using the value of the promise. It is also good practice to throw an exception from the .then handler, which will reject the promise - this is known as promise-throwing safety.
    • In the actual onload case, you should use addEventListener instead of onX.

    reply
    0
  • Cancelreply