2016-08-18 11 views
11

Sie haben ein Prototyp-Objekt Foo mit zwei asynchronen Methodenaufrufen, bar und baz.Verketten von asynchronen Methodenaufrufen - JavaScript

var bob = new Foo() 

Foo.prototype.bar = function land(callback) { 
    setTimeout(function() { 
    callback() 
    console.log('bar'); 
    }, 3000); 
}; 

Foo.prototype.baz = function land(callback) { 
    setTimeout(function() { 
    callback() 
    console.log('baz'); 
    }, 3000); 
}; 

Wir wollen bob.bar() tun. Baz() und haben es "bar" log und "baz" nacheinander.

Wenn Sie die Methodenaufrufe nicht ändern können (einschließlich der Übergabe Ihrer Rückruffunktion), wie können Sie einen Standardrückruf an diese Methodenaufrufe übergeben?

Einige Ideen:

  1. Wrap „bob“ mit Dekorateur

  2. Ändern Konstruktor (noch unscharf, wie zu implementieren, könnte ein kleines Beispiel verwenden) Standard-Callback zu vergeben, wenn keine zugewiesen (haben nicht berücksichtigt, wenn dies möglich ist oder nicht)

  3. Verwenden Sie einen Generator-Wrapper, der weiterhin die nächste Methode aufruft, bis keine mehr übrig sind?

+0

Können Sie angeben, wie Sie verspricht verwenden würde? Sie sind aufgetreten, aber nicht sicher, wie sie angewendet werden –

+0

Sind Sie mit der 'bob.bar(). Baz()' Syntax verheiratet? Es gibt viele Möglichkeiten, die Operation zu beschreiben, aber ich kann mir nichts vorstellen, was mit dieser spezifischen Syntax funktionieren würde. – Mike

+0

Angenommen, wir sind :(Ich denke, wir müssen es irgendwie wickeln –

Antwort

8

Die empfohlene Methode ist stattdessen promises zu verwenden. Dies ist ein gemeinschaftsweiter Trend, asynchrone Inhalte zu erstellen.

Wir wollen bob.bar() tun. Baz() und haben es "bar" log und "baz" nacheinander.

Warum möchten Sie das nur tun, um diese bob.bar().baz() "Syntax" zu erreichen? Wenn Sie es einfach mit der Promise-API tun können, ohne zusätzliche Anstrengungen zu unternehmen, damit diese Syntax funktioniert, die in der Tat die Code-Komplexität erhöht, wodurch der eigentliche Code schwer zu verstehen ist.

So, möchten Sie vielleicht mit dem Versprechen basierten Ansatz etwas wie dieses betrachten, die viel mehr Flexibilität anbietet als das, was Sie von Ihrem Ansatz erreicht hätte:

Foo.prototype.bar = function() { 
    return new Promise(function (resolve) { 
     setTimeout(function() { 
      resolve() 
      console.log('bar'); 
     }, 3000); 
    }; 
}; 

Foo.prototype.baz = function() { 
    return new Promise(function (resolve) { 
     setTimeout(function() { 
      resolve() 
      console.log('baz'); 
     }, 3000); 
    }; 
}; 

Jetzt würden Sie dies tun sie nacheinander nacheinander auszuführen:

var bob = new Foo(); 

bob.bar().then(function() { 
    return bob.baz(); 
}); 

// If you're using ES2015+ you could even do: 
bob.bar().then(() => bob.baz()); 

Wenn Sie mehr Funktionen Kette braucht man einfach tun es:

bob.bar() 
    .then(() => bob.baz()) 
    .then(() => bob.anotherBaz()) 
    .then(() => bob.somethingElse()); 

Wie auch immer, wenn Sie nicht gewohnt sind Versprechen zu verwenden Sie könnten read this

+0

Du darfst die Aufrufsyntax nicht ändern – Bergi

0

wollen Wenn Sie Rückruf Hölle vermeiden wollen und halten Sie Ihre geistige Gesundheit ES6 verspricht die am besten geeignete Ansatz aus Gründen der funktionalen Programmierung sind. Sie verketten Ihre asynchronen sequentiellen Aufgaben einfach in der asynchronen Zeitachse, genau wie in einer synchronen Zeitleiste.

In diesem speziellen Fall müssen Sie nur Ihre asynchronen Funktionen promitieren. Angenommen, Ihre asynch-Funktionen nehmen Daten und einen Rückruf wie asynch(data,myCallback). Nehmen wir an, dass der Rückruf der erste Fehler ist.

wie z.

Wenn Ihre asynch-Funktion promitired ist, werden Sie tatsächlich eine Funktion zurückgegeben, die Ihre Daten übernimmt und eine Zusage zurückgibt. Es wird erwartet, dass Sie myCallback auf der Stufe then anwenden. Der Rückgabewert von myCallback wird dann an die nächste Stufe weitergegeben, wo Sie eine weitere asynch-Funktion aufrufen können, die mit dem Rückgabewert myCallback geliefert wird, und dies geht so lange, wie Sie möchten. Sehen wir uns an, wie wir dieses Abstract in Ihren Workflow implementieren.

function Foo(){} 
 

 
function promisify(fun){ 
 
    return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res))); 
 
} 
 

 
function myCallback(val) { 
 
    console.log("hey..! i've got this:",val); 
 
    return val; 
 
} 
 

 
var bob = new Foo(); 
 

 
Foo.prototype.bar = function land(value, callback) { 
 
    setTimeout(function() { 
 
    callback(false,value*2); // no error returned but value doubled and supplied to callback 
 
    console.log('bar'); 
 
    }, 1000); 
 
}; 
 

 
Foo.prototype.baz = function land(value, callback) { 
 
    setTimeout(function() { 
 
    callback(false,value*2); // no error returned but value doubled and supplied to callback 
 
    console.log('baz'); 
 
    }, 1000); 
 
}; 
 

 
Foo.prototype.bar = promisify(Foo.prototype.bar); 
 
Foo.prototype.baz = promisify(Foo.prototype.baz); 
 

 
bob.bar(1) 
 
    .then(myCallback) 
 
    .then(bob.baz) 
 
    .then(myCallback)

+0

Du darfst nicht um die Aufrufsyntax zu ändern. – Bergi

0

Warnung ist dies nicht ganz richtig vor. Idealerweise würden wir Promise ableiten und eine korrekte then/catch Funktionalität haben, aber es gibt einige Vorbehalte mit der Unterklasse bluebird Promise. Die Idee besteht darin, ein internes Array von Versprechensgenerierungsfunktionen zu speichern, und dann, wenn auf ein Versprechen gewartet wird (dann/darauf wartet), wartet man auf diese Versprechen.

const Promise = require('bluebird'); 

class Foo { 
    constructor() { 
    this.queue = []; 
    } 

    // promise generating function simply returns called pGen 
    pFunc(i,pGen) { 
    return pGen(); 
    } 

    bar() { 
    const _bar =() => { 
     return new Promise((resolve,reject) => { 
     setTimeout(() => { 
      console.log('bar',Date.now()); 
      resolve(); 
     },Math.random()*1000); 
     })  
    } 
    this.queue.push(_bar); 
    return this; 
    } 

    baz() { 
    const _baz =() => { 
     return new Promise((resolve,reject) => { 
     setTimeout(() => { 
      console.log('baz',Date.now()); 
      resolve(); 
     },Math.random()*1000); 
     })  
    } 
    this.queue.push(_baz); 
    return this; 
    } 

    then(func) { 
    return Promise.reduce(this.queue, this.pFunc, 0).then(func); 
    } 
} 


const foo = new Foo(); 
foo.bar().baz().then(() => { 
    console.log('done') 
}) 

Ergebnis:

[email protected]:~/Desktop/Dropbox/code/js/async-chain$ node index.js 
bar 1492082650917 
baz 1492082651511 
done 
Verwandte Themen