The Callback function

In the early JS asynchronous implementation, callback is used to achieve asynchronous tasks. Including event callback, setTimeout, setInterval, Ajax, etc., take this way to write code, functional implementation is no problem, but not elegant and clear, prone to callback hell.

Promise

Promise A plus Chinese specification English specification

Realization Process (I)

Synchronous infrastructure

new Promise(function(resovle, reject){})Copy the code

You can see from the above code that the constructor takes a function.

const PENDING = "pending"; / / wait for
const FULFILLED = "fulfilled"; / / has been completed
const REJECTED = "rejected"; / / have been rejected

function Promise(executor) {
    let self = this;
    self.status = PENDING;

    self.value;
    self.reason;


    function resolve(value) {
        if(self.status === PENDING) { self.status = FULFILLED; self.value = value; }}function reject(reason) {
        if(self.status === PENDING) { self.status = REJECTED; self.reason = reason; }}try { // The specification says that the actuator throws exceptions in reject
        executor(resolve, reject);
    } catch(e) {
        reject(e)
    }
}
Copy the code

Then method implementation

Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;
    /** * Ondepressing and onRejected are both optional parameters. This will be a pity if onFulfilled is not a function, which must be ignored */
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value) {
        return value;
    };
    onRjected = typeof onRjected === 'function' ? onRjected : function(reason) {
        throw reason;
    }
    
    if (self.status === FULFILLED) {
        onFulfilled(self.value);
    }
    if(self.status === REJECTED) { onRjected(self.reason); }}Copy the code

Realization Process (II)

Asynchronous call implementation

The successful methods in the THEN will be executed in sequence after success. The successful callback and failed callback in the THEN can be stored in the array first, and the successful or failed array will be called when success occurs.

// Add the callback queue for then
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
Copy the code
// Call the queue handler on success
self.onResolvedCallbacks.forEach(function (fn, index, array) {
    fn()
});
Copy the code
// Call handler in queue on failure
self.onRejectedCallbacks.forEach(function (fn, index, array) {
    fn()
})
Copy the code

Realization Process (3)

When calling then(), jquery implements the chained call by returning this, but promise does not. Promise implements the chained call by returning a new promise. And the then() method can pass through calls to the next THEN () without wearing anything…

Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;

    let promise2 = null; // Return promise

    // Our code can pass nothing in then
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    };
    
    onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
        throw reason;
    }

    if (self.status === FULFILLED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            let result = onFulfilled(self.value);
            onFulfilled2(result);
        });
    }
    
    if (self.status === REJECTED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            let reason = onRjected(self.reason);
            onRjected2(reason);
        });

    }

    if (self.status === PENDING) {
        promise2 = new Promise(function (resolve, reject) {
            self.onResolvedCallbacks.push(function () {
                let value = onFulfilled(self.value);
                resolve(value);
            });

            self.onRejectedCallbacks.push(function () {
                letreason = onRjected(self.reason); reject(reason); })}); }return promise2;
}
Copy the code

Realization Process (IV)

If there is a successful callback or a failed callback in then, the success of the next THEN will be returned as long as the result (return XXX) is returned. If there is an Error, the failure of the next THEN will be returned.

If the first promise returns a normal value, the successful callback for the next THEN will be carried forward. If the first promise returns a promise, wait for the result of the returned promise to be passed to the next THEN.

function resolvePromise(promise2, x, resolve, reject) {
    // It is possible that the x returned here is someone else's promise
    // Allow other scribbles if possible
    // Return the same result as promise and never succeed or fail
    if (promise2 === x) {
        throw new Error('Can't turn back on yourself')}let called;
    // Determine the Promise type
    if (typeofx ! = ='object' && (typeof x === 'object' || typeof x === 'function')) {
        try {


            let then = x.then;
            // Promise can be a function. There's a then method on the function.
            if (typeof then === 'function') {
                then.call(x, function (y) {
                    if (called) return;
                    called = true;
                    // y may still be a promise, resolved until a normal value is returned
                    resolvePromise(promise2, y, resolve, reject);
                }, function (err) {
                    if (called) return;
                    called = true;
                    reject(err);
                });
            } else {
                // Not a function, another promise
                resolve(x)
            }


        } catch (err) {
            if (called) return
            called = true; reject(err); }}else {
        // Common type returns
        //3. If a successful callback or a failed callback returns a result, the success of the next THEN will be followed.resolve(x); }}Copy the code
Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;

    let promise2 = null;

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    };
    onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
        throw reason;
    }


    if (self.status === FULFILLED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            setTimeout(function () { //9. Promise specification requires that all onFufiled and onRjected be delivered asynchronously,setTimeout
                try {
                    let result = onFulfilled(self.value);
                    resolvePromise(promise2, result, onFulfilled, onRjected);
                } catch (err) {
                    onRjected2(err);// If there are errors go to the next then failure}}); }); }if (self.status === REJECTED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            setTimeout(function () { //9. Promise specification requires that all onFufiled and onRjected be delivered asynchronously,setTimeout
                try {
                    let reason = onRjected(self.reason);
                    resolvePromise(promise2, reason, onFulfilled, onRjected);
                } catch (err) {
                    onRjected2(err);// If there are errors go to the next then failure}}); }); }if (self.status === PENDING) {
        promise2 = new Promise(function (resolve, reject) {
            self.onResolvedCallbacks.push(function () {
                setTimeout(function () { //9. Promise specification requires that all onFufiled and onRjected be delivered asynchronously,setTimeout
                    try {
                        let value = onFulfilled(self.value);
                        resolvePromise(promise2, value, resolve, reject);
                    } catch (err) {
                        reject(err); // If there are errors go to the next then failure}}); }); self.onRejectedCallbacks.push(function () {
                setTimeout(function () { //9. Promise specification requires that all onFufiled and onRjected be delivered asynchronously,setTimeout
                    try {
                        let reason = onRjected(self.reason);
                        resolvePromise(promise2, reason, resolve, reject);
                    } catch (err) {
                        reject(err);// If there are errors go to the next then failure}}); })}); }return promise2;
}
Copy the code

Realization Process (5)

Implement the case methods catch, All, race, resolve, Reject, and defer.

// Catch the wrong method
// equivalent to the first successful callback to not then
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
};

Promise.prototype.all = function (promises) {
    return new Promise(function (resolve, reject) {
        let returns = [];
        let index = 0;

        function processData(i, data) {
            returns[i] = data;
            if (++index === promises.length) {
                resolve(returns)
            }
        }

        for (let i = 0; i <= promises.length; i++) {
            promises[i].then(function (data) { processData(i, data) }, reject); }})};Promise.prototype.race = function (promises) {
    return new Promise(function (resolve, reject) {
        promises.forEach(function (value) { value.then(resolve, reject); }})});Promise.resolve = function (value) {
    return new Promise(function (resolve, reject) {
        resolve(value)
    });
};

Promise.reject = function (reason) {
    return new Promise(function (resolve, reject) {
        reject(reason)
    })
};

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    return new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
};
Copy the code

Genernator

The Genernator function is denoted by *, yield, which splits the function into several parts, calls next and continues, returns an iterator that has a next method, yield is followed by the value of value, The yield equals the value passed in by our current call to next. The first next pass was invalid.

function* read() {
    console.log('init');
    let a = yield 'The first wave of Tangyuan';
    console.log(a);
    let b = yield 'The second wave of Tangyuan'
    console.log(b);
    return b;
}
let it = read();
console.log(it.next('Give me tangyuan'));
// init
// {value:' 1 ',done:false}
console.log(it.next('I want more tangyuan.')); 
// I'd like some glutinous rice balls, too
// {value:' 1 ',done:false}
console.log(it.next('Enough is enough.'));
// That's enough
// {value: 'second wave',done:true}
console.log(it.next('Anything else? '));
// {value:undefined,done:true}
Copy the code

As you can see, when read() is called, the program simply returns an iterator. The code inside the function is not running.

Call next(‘ give me tangyuan ‘) and the function runs like this:

console.log('init');
yield 'The first wave of Tangyuan';
// So say yes
// init
// Return {value:' first tangyuan ',done:false}
// Notice that the 'gimme gnocchi' function passed in the first time is not received.
Copy the code

On the second call to console.log(it.next(‘ I still want tangyuan ‘)), the program runs the following:

let a = 'I want more tangyuan.';
console.log(a);
yield 'The second wave of Tangyuan'
// I'd like some glutinous rice balls, too
// {value:' 1 ',done:false}
// Assign the input parameter to a, print it out, and return yield for 'second wave of glutinous rice balls'.
Copy the code

On the third call to console.log(it.next(‘ Enough, no more ‘)), the program runs the following:

let b = 'Enough is enough.'
console.log(b);
return b;
// That's enough
// {value:‘够了不要了’, done:true}
// Assign the incoming argument to b, print it out, and return b's 'enough no more'. This is the end of the iteration, and the returned done becomes true.
Copy the code

Continue calling console.log(it.next(‘ Anything else? ‘), the program has no iteration content, so it returns directly

// {value:undefined, done:true}
Copy the code

Generator is mainly used with Promise

let bluebird = require('bluebird'); // Bluebird see related function library introduction
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);
function* gRead() {
    let content1 = yield read('./1.txt'.'utf8');
    // content1 holds the address of content2
    let content2 = yield read(content1, 'utf8');
    return content2;
}
// I need to call next again and again to get the contents of content2, please
Copy the code

So with co library

// Co library NPM install co can automatically iterate generator
let co = require('co');
co(gRead()).then(function (data) {
    console.log(data)
})
// Get the result once
Copy the code

But where is the CO library implemented? Let’s simulate a CO library

function co(it) {
    // Return a promise
    return new Promise(function (resolve, reject) {
        function next(d) { // The second recursion comes in with a value.
            let { value, done } = it.next(d);
            // Value is a promise, and the first call to pass the value is useless.
            if(! done) {// If no traversal is completed
                value.then(function (data) {
                // The content2 promise runs, passing in the read result.
                    next(data)
                }, reject)
            } else {
                resolve(value); 
            }
        }
        next();
    });
}
Copy the code

As you can see, the CO library recursively moves every next() in the iterator until done===true.

async-await

Async-await can be understood as syntactic sugar for CO + generator. Aysnc needs await, await only promise.

async function aRead(){
    try{
        let content1 = await read('./1.txt'.'utf8');
        let content2 = await read(content1,'utf8');
        return content2;
    }catch(e){ // Catch if something goes wrong
        console.log('err',e)
    }
}
Copy the code

The function call is the same as promise, as follows:

// Async returns promise,
aRead().then(function (data) {
    console.log('print', data);
}, function (err) {
    console.log('error', err);
});
Copy the code

What problems does async/await solve?

  • Call back to hell.
  • Perform asynchrony concurrently, synchronizing the result promise.all at the same time.
  • To resolve the return value problem, yield returns are passed in by calling the function.
  • You can implement a try/catch of your code.

Correlation function library

co

bluebird

/ blueBird
// npm install bluebird
let fs = require('fs');
let bluebird = require('bluebird');
function promisify(fn) { // Promisification handles the callback internally
    return function (. args) {
        return new Promise(function (resolve, reject) { fn(... args,function (err, data) {
                if(err) reject(err); resolve(data); }}})})function promisifyAll(obj) {
    Object.keys(obj).forEach(key= > { // es5 converts an object to an array
        if (typeof obj[key] === 'function') {
            obj[key + 'Async'] = promisify(obj[key])
        }
    })
}
promisifyAll(fs); // Add a promise to all methods
fs.readFileAsync('./1.txt'.'utf8').then(function (data) {
    console.log(data);
});
Copy the code

promises-aplus-tests