首頁  >  問答  >  主體

將標題重寫為:如何將現有的回呼 API 轉換為 Promise 形式?

<p>我想用promises來處理,但我的回呼API的格式如下:</p> <h3>1. DOM載入或其他一次性事件:</h3> <pre class="brush:php;toolbar:false;">window.onload; // 設定為回呼函數 … window.onload = function() { };</pre> <h3>2. 普通回呼函數:</h3> <pre class="brush:php;toolbar:false;">function request(onChangeHandler) { … } request(function() { // 發生了變化 … });</pre> <h3>3. Node風格的回呼函數("nodeback"):</h3> <pre class="brush:php;toolbar:false;">function getStuff(dat, callback) { … } getStuff("dataParam", function(err, data) { … })</pre> <h3>4. 整個函式庫都是使用Node風格的回呼函數:</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>我如何使用promises來處理這個API,如何"promisify"它? </h3>
P粉268654873P粉268654873425 天前377

全部回覆(2)我來回復

  • P粉618358260

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

    今天,我可以在Node.js中使用Promise作為一個普通的Javascript方法。

    一個簡單且基本的Promise範例(使用KISS方法):

    普通的Javascript非同步API程式碼:

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

    Promise的Javascript非同步API程式碼:

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

    (我推薦訪問這個優秀的來源

    此外,Promise也可以與ES7中的async\await一起使用,使程式流程等待fulfilled結果,如下所示:

    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()方法运行代码

    使用相同的程式碼,可以使用.then()方法:

    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也可以在任何基於Node.js的平台上使用,例如react-native

    額外獎勵:一種混合方法
    # (假設回呼方法有兩個參數,分別是錯誤和結果)

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

    上述方法可以同時回應舊式的回呼和Promise用法的結果。

    希望這能幫到你。

    回覆
    0
  • P粉680087550

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

    承諾具有狀態,它們開始時處於掛起狀態,可以解決為:

    • 已實現表示計算成功完成。
    • 已拒絕表示計算失敗。

    傳回承諾的函數不應該拋出例外,而應該回傳拒絕。從傳回承諾的函數中拋出例外狀況將強制您同時使用} catch { .catch。使用承諾化的API的人們不希望承諾拋出異常。如果您不確定JS中的非同步API如何運作,請先查看此答案

    1. DOM載入或其他一次性事件:

    因此,創建承諾通常意味著指定它們何時解決-這意味著它們何時移動到已實現或已拒絕階段以指示資料可用(並可使用.then存取)。

    使用支援Promise建構子的現代承諾實作(如原生ES6承諾):

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

    然後您可以這樣使用生成的承諾:

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

    使用支援延遲(這裡我們使用$q作為範例,但稍後我們也會使用jQuery)的函式庫:

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

    或使用類似jQuery的API,鉤住一次發生的事件:

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

    2. 普通回呼:

    這些API相當常見,因為在JS中回呼很常見。讓我們來看看常見情況下的onSuccessonFail

    function getUserData(userId, onLoad, onFail) { …

    使用支援Promise建構子的現代承諾實作(如原生ES6承諾):

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

    使用支援延遲(這裡我們使用jQuery作為範例,但我們之前也使用了$q)的函式庫:

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

    jQuery也提供了$.Deferred(fn)形式,它的優點是允許我們寫一個非常接近new Promise(fn)形式的表達式,如下所示:

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

    注意:這裡我們利用了jQuery延遲的resolvereject方法是「可分離」的事實;即它們綁定到jQuery.Deferred()的實例。並非所有庫都提供此功能。

    3. Node風格回呼("nodeback"):

    Node風格回呼(nodebacks)具有特定的格式,其中回呼始終是最後一個參數,其第一個參數是錯誤。首先手動將其轉換為承諾:

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

    使用延遲,您可以執行以下操作(我們使用Q作為範例,儘管Q現在支援新語法您應該優先選擇該語法):

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

    一般來說,您不應該手動過多地將事物轉換為承諾,大多數針對Node設計的承諾庫以及Node 8 中的原生承諾都具有用於將nodebacks轉換為承諾的內建方法。例如

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

    4. 整個函式庫使用Node風格回呼:

    這裡沒有黃金法則,您可以逐一將它們轉換為承諾。但是,某些承諾實作允許您批量執行此操作,例如在Bluebird中,將nodeback API轉換為承諾API就像這樣簡單:

    Promise.promisifyAll(API);

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

    注意:

    • 當您在.then處理程序中時,當然不需要將事物轉換為承諾。從.then處理程序傳回一個承諾將使用該承諾的值解決或拒絕。從.then處理程序中拋出異常也是良好的實踐,將拒絕該承諾-這就是著名的承諾拋出安全性。
    • 在實際的onload情況中,您應該使用addEventListener而不是onX

    回覆
    0
  • 取消回覆