Mevcut bir geri arama API'sını vaatlere nasıl dönüştürebilirim?


721

Ben vaatlerle çalışmak istiyorum ama ben gibi bir biçimde geri çağrı API var:

1. DOM yükü veya başka bir defalık etkinlik:

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

};

2. Düz geri arama:

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

3. Düğüm stili geri arama ("düğüm"):

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

4. Düğüm stili geri çağrıları olan bütün bir kütüphane:

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

API ile vaatlerde nasıl çalışırım, nasıl "söz veriyorum"?


Kendi cevabımı gönderdim, ancak belirli bir kütüphane veya daha fazla koşulda bunun nasıl yapılacağı konusunda genişleyen cevaplar da çok hoş geldiniz.
Benjamin Gruenbaum

@Bergi Bu ilginç bir fikir, iki ortak yaklaşımı (Promise yapıcısı ve ertelenmiş nesne) kullanan genel bir cevap vermeye çalıştım. Cevaplara iki alternatif vermeye çalıştım. RTFMing'in bu sorunu çözdüğünü kabul ediyorum, ancak hem burada hem de hata izleyicide bu konuyla sık sık karşılaşıyoruz, bu yüzden 'kanonik bir soru' yerinde olduğunu düşündüm - RTFMing, JS etiketindeki sorunların yaklaşık% 50'sini çözdüğünü düşünüyorum: D bir cevaba katkıda bulunmak veya çok takdir edeceğiniz bir düzenleme yapmak için ilginç bir anlayışınız var.
Benjamin Gruenbaum

new PromiseEklenti oluşturmak önemli bir ek yük oluşturuyor mu? Düğüm uygulamamdaki tüm eşzamanlı kodu kaldırmak için tüm eşzamanlı Noje.js işlevlerini bir Sözde sarmak istiyorum, ama bu en iyi yöntem mi? Başka bir deyişle, statik bir argümanı (örneğin bir dize) kabul eden ve hesaplanan bir sonuç döndüren bir işlev, bunu bir söze mi sarmalıyım? ... Nodejs'de eşzamanlı kod olmamalı bir yerde okudum.
Ronnie Royston

1
@RonRoyston no, senkron çağrıları vaatlerle sarmak iyi bir fikir değildir - sadece G / Ç gerçekleştirebilen asenkron çağrıları
Benjamin Gruenbaum

Yanıtlar:


745

Vaatlerin durumu vardır, beklemede olarak başlarlar ve şunlara yerleşebilirler:

  • yerine hesaplama başarıyla tamamlandı anlamına gelir.
  • hesaplama başarısız olduğu için reddetti .

Söz döndüren işlevler asla atmamalı , bunun yerine reddetmeleri döndürmelidir. Bir söz dönen işlevinden Fırlatma a ikisini de kullanmak için zorlar } catch { ve bir .catch. Vaat edilmiş API'leri kullanan kişiler vaatlerin verilmesini beklemezler. Zaman uyumsuz API'ların JS'de nasıl çalıştığından emin değilseniz - lütfen önce bu cevaba bakın .

1. DOM yükü veya başka bir defalık etkinlik:

Bu nedenle, vaatler oluşturmak genellikle ne zaman yerleşeceklerini belirtmek anlamına gelir - bu da verilerin kullanılabilir olduğunu (ve bunlara erişilebildiğini .then) belirtmek için yerine getirilen veya reddedilen aşamaya geçtikleri anlamına gelir .

PromiseDoğal ES6 vaatleri gibi kurucuyu destekleyen modern vaat uygulamaları ile :

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

Daha sonra ortaya çıkan sözü aşağıdaki gibi kullanırsınız:

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

Ertelemeyi destekleyen kitaplıklarla (Burada bu örnek için $ q kullanalım, ancak daha sonra jQuery kullanacağız):

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

Veya API gibi bir jQuery ile bir kez gerçekleşen bir etkinliğe bağlanma:

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

2. Düz geri arama:

Bu API'lar oldukça yaygındır çünkü JS'de geri aramalar yaygındır. Ortak sahip olma durumuna bakalım onSuccessve onFail:

function getUserData(userId, onLoad, onFail) { 

PromiseDoğal ES6 vaatleri gibi kurucuyu destekleyen modern vaat uygulamaları ile :

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

Ertelemeyi destekleyen kütüphanelerle (burada bu örnek için jQuery kullanalım, ancak yukarıda $ q da kullandık):

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

jQuery , aşağıdaki gibi $.Deferred(fn), new Promise(fn)formu çok yakından taklit eden bir ifade yazmamıza olanak sağlayan bir form da sunar :

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

Not: Burada, bir jQuery ertelenmiş yöntem resolveve rejectyöntemlerin "ayrılabilir" olduğu gerçeğini kullanırız ; yani. jQuery.Deferred () örneğine bağlıdırlar . Tüm kütüphaneler bu özelliği sunmaz.

3. Düğüm stili geri arama ("düğüm"):

Düğüm stili geri çağrıları (düğüm geri çağrıları), geri çağrıların her zaman son argüman olduğu ve ilk parametresinin bir hata olduğu belirli bir biçime sahiptir. İlk önce manuel olarak söz verelim:

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

Kime:

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

Ertelemelerle aşağıdakileri yapabilirsiniz (Q şimdi tercih etmeniz gereken yeni sözdizimini desteklese de, bu örnek için Q kullanalım ):

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

Genel olarak, şeyleri çok fazla elle vaat etmemelisiniz, Düğüm göz önünde bulundurularak tasarlanan çoğu söz kitaplığı ve Düğüm 8+'daki yerel sözlerin düğüm geri dönüşlerini vaat etmek için yerleşik bir yöntemi vardır. Örneğin

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

4. Düğüm stili geri çağrıları olan bütün bir kütüphane:

Burada altın bir kural yok, onları tek tek söz ediyorsunuz. Bununla birlikte, bazı vaat uygulamaları bunu örneğin Bluebird'de toplu olarak yapmanıza izin verir, bir geri dönüş API'sini bir söz API'sına dönüştürmek şu kadar basittir:

Promise.promisifyAll(API);

Veya ile yerli vaatler içinde Düğüm :

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

Notlar:

  • Tabii ki, bir .thenişleyicide olduğunuzda şeyleri vaat etmeniz gerekmez. Bir .thenişleyiciden bir sözün geri verilmesi söz konusu değerin değerine çözümlenecek veya reddedilecektir. Bir .thenişleyiciden atmak da iyi bir uygulamadır ve sözünü reddedecektir - bu ünlü söz atma güvenliği.
  • Gerçek bir onloaddurumda, kullanmak addEventListeneryerine kullanmalısınız onX.

Benjamin, düzenleme davetinizi kabul ettim ve vaka 2'ye başka bir jQuery örneği ekledim. Umarım beğenirsin.
Roamer-1888

@ Roamer-1888 Görmediğim ve zamanında kabul etmediğim için reddedildi. Değer için ne kadar yararlı olsa da ek çok alakalı olduğunu sanmıyorum.
Benjamin Gruenbaum

2
Benjamin, olsun veya olmasın resolve()ve reject()yeniden kullanılabilir olması için yazılmıştır, bunu formun bir jQuery örneğini sunmaktadır çünkü benim önerilen düzenleme alakalı olduğunu girişim $.Deferred(fn)aksi eksik. Yalnızca bir jQuery örneği dahil edilirse var d = $.Deferred();, insanların vb. İhmal edilmiş $.Deferred(fn)formu kullanmaya teşvik edilmeleri gerektiğinden, bunun yerine vb. Şeklinde olmasını öneririm. kullanmak kütüphanelerini Açığa Çıkarma Oluşturucu Desen .
Roamer-1888

Heh,% 100 adil olmak için jQuery bunu yapmanıza izin vermiyordum $.Deferred(fn), bunu önümüzdeki 15 dakika içinde mevcut örnek yerine düzenlemek isterseniz, zamanında onaylamaya çalışacağımdan eminim :)
Benjamin Gruenbaum

7
Bu harika bir cevap. Ayrıca util.promisifyNode.js'nin RC 8.0.0'dan itibaren çekirdeğine ekleneceğini de belirterek güncellemek isteyebilirsiniz . Çalışması Bluebird'den çok farklı değildir Promise.promisify, ancak sadece yerel Promise istemeniz durumunda ek bağımlılıklar gerektirmeme avantajına sahiptir. Konu hakkında daha fazla bilgi almak isteyen herkes için util.promisify hakkında bir blog yazısı yazdım .
Bruno

55

Bugün, kullanabilir Promiseiçinde Node.jsdüz JavaScript yöntemi olarak.

Basit ve temel bir örnek Promise( KISS yolu ile):

Düz Javascript Async API kodu:

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

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

    successCallback( number / divider )

}

Promise Javascript Async API kodu:

function divisionAPI (number, divider) {

    return new Promise(function (fulfilled, rejected) {

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

        fulfilled( number / divider )

     })

}

( Bu güzel kaynağı ziyaret etmenizi öneririm )

Ayrıca Promisetoplu olarak birlikte kullanılabilir async\awaitiçinde ES7bir program akış beklemesi için fullfiledaşağıdaki gibi sonuç:

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

.then()Yöntemi kullanarak aynı kodla başka bir kullanım

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

PromiseNode.js tabanlı herhangi bir platformda da kullanılabilir react-native.

Bonus : Karma bir yöntem
(Geri arama yönteminin hata ve sonuç olarak iki parametresi olduğu varsayılır)

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 )

     })

}

Yukarıdaki yöntem eski moda geri arama ve Promise kullanımları için sonucu yanıtlayabilir.

Bu yardımcı olur umarım.


3
Bunlar vaatlere nasıl dönüştürüleceğini göstermiyor.
Dmitri Zaitsev

33

Bir işlevi söz olarak dönüştürmeden önce 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)
})

Dönüştürdükten Sonra

var request = require('request');

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

Birden fazla isteği yerine getirmeniz gerekiyorsa

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

23

window.onloadYüklemeden sonra çağrılıp çağrılmadığını tespit etmediğinden, @Benjamin tarafından sunulan önerinin her zaman çalışacağını düşünmüyorum . Birçok kez ısırıldım. İşte her zaman çalışması gereken bir sürüm:

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

1
"zaten tamamlanmış" dalın eşzamansız olarak çağrıldığından emin olmak için setTimeout(resolve, 0)(veya setImmediatevarsa) kullanmaması gerekir mi?
Alnitak

5
@Alnitak resolveSenkronize çağrı yapmak iyidir. Söz verenin thenişleyicileri, eşzamanlı olarak çağrılıp çağrılmadığına bakılmaksızın , çerçeve ile eşzamansızresolve olarak çağrılabilir.
Jeff Bowman

15

Node.js 8.0.0, util.promisify()standart Node.js geri arama stili API'lerinin bir Promise döndüren bir işleve sarılmasına izin veren yeni bir API içerir . util.promisify()Aşağıda örnek bir kullanım gösterilmiştir.

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

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

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

Bkz . Vaatler için geliştirilmiş destek


2
Bunu açıklayan iki cevap zaten var, neden üçüncü bir cevap gönderelim?
Benjamin Gruenbaum

1
Düğümün bu sürümü yayınlandığından ve "resmi" özellik açıklaması ve bağlantısını bildirdim.
Gian Marco Gherardi

14

Node.js 8.0.0 için sürüm adayında, herhangi bir işlevi vaat etme kapasitesini kapsayan yeni bir yardımcı program var util.promisify( util.promisify hakkında yazdım ).

Diğer cevaplarda önerilen yaklaşımlardan çok farklı değildir, ancak çekirdek bir yöntem olma avantajına sahiptir ve ek bağımlılıklar gerektirmez.

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

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

Sonra readFileyerel bir döndüren bir yöntem var Promise.

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

1
Hey, ben (OP) aslında util.promisifyiki kez önerdim (bu sorunun yazıldığı 2014'te ve birkaç ay önce - Düğümün çekirdek üyesi olarak ittim ve Düğüm'de mevcut sürümüm). Henüz kamuya açık olmadığından, henüz bu cevaba eklemedim. Yine de kullanım geri bildirimlerini derinden takdir eder ve sürüm için daha iyi dokümanlar elde etmek için bazı tuzakların ne olduğunu öğreniriz :)
Benjamin Gruenbaum

1
Ayrıca, util.promisifyblog yayınınızda vaat etmek için özel bayrağı tartışmak isteyebilirsiniz :)
Benjamin Gruenbaum

@BenjaminGruenbaum util.promisify.customSembolü kullanarak util.promisify sonucunu geçersiz kılmanın mümkün olduğu anlamına mı geliyorsunuz? Dürüst olmak gerekirse, bu kasıtlı bir özlüydü, çünkü henüz yararlı bir kullanım durumu bulamıyorum. Belki bana bazı girdiler verebilirsin?
Bruno

1
Elbette, fs.existsDüğüm kuralına uymayan API'lar veya API'ları düşünün - bir mavi kuş Promise.promisify onları yanlış util.promisifyanlar , ancak doğru yapar.
Benjamin Gruenbaum

7

Node JS ile JavaScript yerel vaatlerini kullanabilirsiniz.

Cloud 9 kod bağlantım: 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

7

Sade eski vanilya javaScript ile, api geri aramasını vaat etmek için bir çözüm.

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

6

Kriskowal'in Q kütüphanesi, geri çağrı vaat eden fonksiyonları içerir. Bunun gibi bir yöntem:

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

Q.ninvoke ile dönüştürülebilir

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

1
Kanonik cevap zaten bahsediyor Q.denodeify. Kütüphane yardımcılarını vurgulamamız gerekiyor mu?
Bergi

3
Ben burada Q açar promisifying hakkında bir google olarak yararlı buldum
Ed Sykes

4

Geri arama alan birkaç fonksiyonunuz olduğunda ve bunun yerine bir söz vermelerini istediğinizde, dönüşüm yapmak için bu fonksiyonu kullanabilirsiniz.

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

4

Vaatler ve zaman uyumsuzlaştırılmış v7.6 + düğümü altında:

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

Nasıl kullanılır:

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

Node.js 8'de yapabilirsiniz promisify nesne yöntemlerini anında bu npm modülü kullanarak:

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

Util.promisify ve Proxies kullanır, böylece nesneleriniz değişmeden kalır. Notlama ayrıca WeakMaps kullanılarak yapılır). İşte bazı örnekler:

Nesnelerle:

const fs = require('fs');
const doAsync = require('doasync');

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

Fonksiyonları ile:

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

Yerel callve applybazı bağlamları bağlamak için bile kullanabilirsiniz :

doAsync(myFunc).apply(context, params)
  .then(result => { /*...*/ });

2

SetTimeout ile örnek olarak ilgilenmek için ES6'da yerel Promise kullanabilirsiniz :

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

}

Bu örnekte, Sözün başarısız olmak için bir nedeni yoktur, bu yüzden reject()asla çağrılmaz.


2

Geri arama stili işlevi her zaman böyle (node.js'deki neredeyse tüm işlevler bu stildir):

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

Bu stil aynı özelliğe sahiptir:

  1. geri arama işlevi son argüman tarafından iletilir.

  2. geri çağrı işlevi her zaman hata nesnesini ilk bağımsız değişken olarak kabul eder.

Yani, aşağıdaki gibi bu stile sahip bir işlevi dönüştürmek için bir işlev yazabilirsiniz:

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

Daha özlü olması için yukarıdaki örnekte ramda.js kullanılmıştır. Ramda.js, işlevsel programlama için mükemmel bir kütüphanedir. Yukarıdaki kodda, uygula (javascript gibi function.prototype.apply) ve append (javascript gibi function.prototype.push) kullandık. Yani, bir geri arama stili işlevini şimdi stil işlevine söz verebiliriz:

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

toPromise ve checkErr işlevi çılgına dönme kütüphanesine aittir , ramda.js (benim oluşturduğum) tarafından işlevsel bir programlama kütüphanesi çatalıdır .

Umarım bu cevap sizin için yararlıdır.


2

Böyle bir şey yapabilirsin

// @flow

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

export default toPromise

Sonra kullan

async loadData() {
  const friends = await toPromise(FriendsManager.loadFriends)

  console.log(friends)
}

2
Hey, bunun mevcut cevaplara ne eklediğinden emin değilim (belki açıklığa kavuşabilir mi?). Ayrıca, söz vericisinin içindeki try / catch'e gerek yoktur (bunu sizin için otomatik olarak yapar). Bunun hangi işlevler için çalıştığı da belli değil (bu, geri
dönüşü


1

Bir callbackfonksiyonun benim promisify sürümü Pfonksiyon:

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

PFonksiyon geri arama imzası olması gerektiğini gerektirir callback(error,result).


1
Bunun yerel vaatlere veya yukarıdaki cevaplara göre ne avantajı var?
Benjamin Gruenbaum

Yerli vaat etmek için ne demek istiyorsun?
loretoparisi


ah evet elbette :). Sadece ve temel fikri göstermek için örnek. Aslında, yerel olanın bile işlev imzasının nasıl tanımlanması (err, value) => ...gerektiğini veya özel bir tanesini nasıl tanımlaması gerektiğini görebilirsiniz (bkz. Özel tanımlı işlevler). Teşekkürler iyi catcha.
loretoparisi

1
@loretoparisi FYI, var P = function (fn, ...args) { return new Promise((resolve, reject) => fn.call(this, ...args, (error, result) => error ? reject(error) : resolve(result))); };seninkiyle aynı şeyi yapardı ve çok daha basit.
Patrick Roberts

1

Aşağıda bir işlevin (geri arama API'sı) bir vaat haline nasıl dönüştürülebileceğinin uygulanması yer almaktadır.

function promisify(functionToExec) {
  return function() {
    var array = Object.values(arguments);
    return new Promise((resolve, reject) => {
      array.push(resolve)
      try {
         functionToExec.apply(null, array);
      } catch (error) {
         reject(error)
      }
    })
  }
}

// USE SCENARIO

function apiFunction (path, callback) { // Not a promise
  // Logic
}

var promisedFunction = promisify(apiFunction);

promisedFunction('path').then(()=>{
  // Receive the result here (callback)
})

// Or use it with await like this
let result = await promisedFunction('path');

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.