web3.js con API prometida

Me gustaría eliminar el dolor del infierno de devolución de llamada de desarrollo web3 y usar el patrón Promise y las palabras clave async/await tanto en el lado del cliente como en Node.js.

¿Existen compilaciones o bifurcaciones de web3.js que implementarían un patrón de promesa en lugar de un patrón de devolución de llamada? por ejemplo se podría decir

let result = await myfilter.get()

En vez de:

 myfilter.get(function cb(error, res) {});
Una pregunta similar declarada fuera de tema: ethereum.stackexchange.com/questions/46197/…
Un comentario válido @TrevorOakley: creo que en lugar de cerrar la pregunta, debería haberse migrado a StackOverflow, que es un mejor medio para tratar preguntas que claramente necesitan más tutoría de programación.
Solo tengo una queja con los diferentes estándares. Para los desarrolladores sin equipo, estos foros son muy importantes. Por lo tanto, debe haber una amplia libertad para publicar preguntas.
Sí. En lugar de cerrar las preguntas, debe haber al menos un comentario donde obtener más ayuda si las personas que cierran la pregunta sienten que hay algún problema con la pregunta.
Mi único problema son los diferentes estándares para diferentes personas. Eso parece estar basado en el representante de lo que he visto.
Estos se pueden discutir y se pueden agregar más pautas (opciones de cierre) a través de Meta discusión. Puedo plantear esta pregunta allí.
He visto varias veces que alguien que tiene una reputación alta publica una pregunta que es aceptada y luego alguien que tiene una reputación baja tiene su pregunta cerrada por ser irrelevante y, sin embargo, está en la misma categoría que la persona que tiene una reputación alta. reputación. Mi punto no es sobre la migración sino sobre el doble rasero.

Respuestas (5)

Web3 1.0 proporciona promesas además de devoluciones de llamada:

Para ayudar a web3 a integrarse en todo tipo de proyectos con diferentes estándares, proporcionamos múltiples formas de actuar en funciones asíncronas. La mayoría de los objetos web3.js permiten una devolución de llamada como último parámetro, además de devolver promesas a funciones de cadena.

https://web3js.readthedocs.io/en/v1.2.1/callbacks-promises-events.html

De hecho, debido a los 'promiEvents' de ethereum, puede usar promesas para desglosar las transacciones en cada paso de la transacción:

web3.eth.sendTransaction({from: '0x123...', data: '0x432...'})
.once('transactionHash', function(hash){ ... })
.once('receipt', function(receipt){ ... })
.on('confirmation', function(confNumber, receipt){ ... })
.on('error', function(error){ ... })
.then(function(receipt){
    // will be fired once the receipt its mined
});
@SubhodI npm show web3 versionsentoncesnpm i install web3@1.0.0

Esto se puede lograr con un contenedor simple:

const promisify = (inner) =>
  new Promise((resolve, reject) =>
    inner((err, res) => {
      if (err) { reject(err) }

      resolve(res);
    })
  );

Para usarlo, simplemente envuelva sus llamadas web3 de la siguiente manera:

const accounts = await promisify(cb => web3.eth.getAccounts(cb));

Si desea prometer todos los métodos web3, puede usar un Proxy ES6:

// simple proxy to promisify the web3 api. It doesn't deal with edge cases like web3.eth.filter and contracts.
const proxiedWeb3Handler = {
  // override getter                               
  get: (target, name) => {              
    const inner = target[name];                            
    if (inner instanceof Function) {                       
      // Return a function with the callback already set.  
      return (...args) => promisify(cb => inner(...args, cb));                                                         
    } else if (typeof inner === 'object') {                
      // wrap inner web3 stuff                             
      return new Proxy(inner, proxiedWeb3Handler);         
    } else {                                               
      return inner;                                        
    }                                                      
  },                                                       
};                                                         

const proxiedWeb3 = new Proxy(web3, proxiedWeb3Handler);

Y ahora usa web3 como una API prometida:

const accounts = await proxiedWeb3.eth.getAccounts();
la mejor respuesta
¡Este comentario me ayudó un montón! Sin embargo, me encontré con un error al tratar de usar awaitcomo sugiere el autor. Mi consola dice que await can only be used with asyn functions... no estoy seguro de por qué estaba ocurriendo ese error. En su lugar, creé un montón de "promesas" usando el envoltorio y lo vinculé a `Promise.all([p1,p2...]).then(function(results))... Esto funcionó para obtener múltiples async funciones para trabajar juntos
Las declaraciones de @ShawnTabrizi awaitdeben estar en una asyncfunción. doc
Totalmente entiende eso. Sin embargo, seguir el patrón que sugiere en su publicación conduce a un error. Tal vez me estoy perdiendo algo
@ShawnTabrizi Abra una pregunta en stackoverflow y vincúlela aquí.
Mover la marca de respuesta correcta a la respuesta Web 1.0, ya que ahora es la versión actual.
@MikkoOhtamaa ¿Los documentos todavía dicen que 0.xx es actual? web3js.readthedocs.io/en/1.0/primeros pasos.html
Intenté esta solución, obtuve un error: se excedió el tiempo de espera de 300000 ms. Para pruebas asíncronas y ganchos, asegúrese de llamar a "done()"; si devuelve una Promesa, asegúrese de que se resuelva.

Si desea tener promesas con contratos, eche un vistazo a Truffle Artifactor .

Para los métodos asincrónicos básicos de Web3, hice este Gist: https://gist.github.com/xavierlepretre/90f0feafccc07b267e44a87050b95caa

Pego aquí la versión al momento de escribir:

módulo.exportaciones = {
    prometer: función (web3) {
        // Canaliza los valores de una devolución de llamada de Web3.
        var callbackToResolve = función (resolver, rechazar) {
            función de retorno (error, valor) {
                    si (error) {
                        rechazar (error);
                    } más {
                        resolver (valor);
                    }
                };
        };

        // Lista de funciones síncronas enmascaradas como valores.
        var syncGetters = {
            db: [],
            eth: [ "cuentas", "número de bloque", "coinbase", "precio del gas", "tasa de hash",
                "minería", "versión de protocolo", "sincronización"],
            net: [ "escuchando", "peerCount" ],
            personal: [ "listaCuentas" ],
            shh: [],
            versión: [ "ethereum", "red", "nodo", "susurro"]
        };

        Object.keys(syncGetters).forEach(función(grupo) {
            Object.keys(web3[grupo]).forEach(función (método) {
                if (syncGetters[grupo].indexOf(método) > -1) {
                    // Saltar
                } else if (tipo de web3[grupo][método] === "función") {
                    web3[grupo][método + "Promesa"] = función () {
                        var argumentos = argumentos;
                        return new Promise(función (resolver, rechazar) {
                            argumentos[argumentos.longitud] = callbackToResolve(resolver, rechazar);
                            argumentos.longitud++;
                            web3[grupo][método].apply(web3[grupo], args);
                        });
                    };
                }
            });
        });
    },
};
Interesante. Creo que también se podría hacer una implementación más limpia utilizando objetos Proxy si uno apunta a las últimas versiones de ECMAScript developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@MikkoOhtamaa Consulte mi respuesta para una implementación utilizando Proxies ES6.

La función web3.eth.sendTransaction devuelve Promesa + Evento combinados.

  • Promesa de recibo de devolución
  • El evento nos permite suscribirnos también para recibo , transacciónHash , confirmación , error

Además, podemos usar devoluciones de llamada que corresponden a los eventos anteriores.

Para más información, ver:

//-------------------------------------

si usamos async/await con sendTransaction, devuelve un recibo . Pero sucederá solo cuando el recibo de la transacción esté listo. Esto puede tomar algo de tiempo.

Si deseamos obtener alguno de los resultados que proporcionan los eventos, podemos envolver la llamada de sendTransaction mediante una promesa personalizada.

Por ejemplo, podemos obtener la transacción Hash que regresa inmediatamente de esta manera:

function sendTransactionTxHashHelper(from: string, to: string, value: string) {
    return new Promise((resolve, reject) => {

        this.web3.eth.sendTransaction({
            from: from,
            to: to,
            value: value,
        }).on('transactionHash', (transactionHash) => {
            resolve(transactionHash);
        });
    });
}

let transactionHash;

try {
    transactionHash = await this.sendTransactionTxHashHelper(
        '0x1234',
        '0x5678',
        '10000000'
    );
} catch (e) {
    console.log(`Error: ${e.name}:${e.message}`, e.stack);
}

console.log('transactionHash', transactionHash);
La pregunta original tiene casi 2 años y se hizo antes de que web3 v1.0-beta fuera público.

En realidad, es un parche bastante simple de escribir. Yo mismo escribí uno hace un tiempo, porque estaba cansado de lidiar con las devoluciones de llamadas. También permite que los envoltorios agradables hagan cosas como desbloquear una cuenta, hacer algunas cosas y luego bloquear la cuenta nuevamente, por ejemplo:

var readFile = Promise.denodeify(fs.readFile);

function whileUnlocked(web3, account, pwfile, task) {
  return readFile(pwfile, 'utf8')
    .then(function(pw) {
      return web3.personal.unlockAccount(account, pw.trim())
        .then(task)
        .then(function() {
          return web3.personal.lockAccount(account);
        });
    });
}

Ver: https://github.com/DeviateFish/web3.js/pull/1

Tenga en cuenta que escribí esto hace algún tiempo, y esta solución definitivamente podría mejorarse con funciones de idioma más nuevas, o sin usar la biblioteca de relleno polivalente de Promise que estoy usando (por ejemplo, aplicar versiones más nuevas de nodo), por lo que su kilometraje puede variar.

[E] Debo agregar que este enfoque no toca la API de filtros, ya que no es estrictamente compatible con el estilo Promise. Las promesas generalmente se refieren a cosas que se realizan de forma asincrónica una vez, no (potencialmente) varias veces. No estaba seguro de cómo abordar mejor esa discrepancia, así que los dejé solos.