promise

JavaScript所有代码都是单线程执行的,所以avaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现
Js 的异步提高了程序的执行效率,同时也减少了程序的可读性。


回调陷阱

异步操作会在将来的某个时间点触发一个函数调用
AJAX就是典型的异步操作

es6 promise

Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。
promiseA+规范中文 英文

// promise用法
new Promise(function (resolve, reject) {
    log('start new Promise...');
    var timeOut = Math.random() * 2;
    log('set timeout to: ' + timeOut + ' seconds.');
    setTimeout(function () {
        if (timeOut < 1) {
            log('call resolve()...');
            resolve('200 OK');
        }
        else {
            log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);
}).then(function (r) {
    log('Done: ' + r);
}).catch(function (reason) {
    log('Failed: ' + reason);
});
// promise all
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 获得一个Array: ['P1', 'P2']
});
// promise race
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});
同步回调和异步回调
// 同步回调
var arr = [1,2,3];

arr.forEach(function (x) {
  console.log('first');
});

console.log('last');

// first first first
// last
// 异步回调
setTimeout(function () {
  console.log('last')
}, 1000);

console.log('first');

// first
// last
实现一个简单的promise

promiseA+规范的内容

  • Promise 本质是一个状态机。每个 promise 只能是 3 种状态中的一种:pending、fulfilled 或 rejected。状态转变只能是 pending -> fulfilled 或者 pending -> rejected。状态转变不可逆。
  • then 方法可以被同一个 promise 调用多次。
  • then 方法必须返回一个 promise。规范里没有明确说明返回一个新的 promise 还是复用老的 promise(即 return this),大多数实现都是返回一个新的 promise,而且复用老的 promise 可能改变内部状态,这与规范也是相违背的。

创建MyPromise类并根据规范初始化状态


image.png

promise的链式调用


image.png
实现一个
const log = console.log;

// 1.创建类
class MyPromise {
    constructor(executor) {
        // 2.等待状态
        this.status = "pending";
        // 初始化data
        this.data = undefined;
        // 初始化reason
        this.reason = undefined;
        // 3.执行函数executor,定义我们的resolve,reject回调
        let resolve = (data) => {
            // log('resolve executor: '+ data);
            // 为了防止多次改变状态
            if(this.status === 'pending') { 
               this.status = 'resolved'
                this.data = data;
                this.onFulFilledCallbacks.forEach((fn) => {
                   fn()
                })
            }
        }
        let reject = (reason) => { 
           // 为了防止多次改变状态
            if(this.status === 'pending') {
                this.status = 'rejected'
                this.reason = reason;
                this.onRejectedCallbacks.forEach((fn) => {
                   fn()
                })
            }
        }

        // 5.储存fulfilled的回调
        this.onFulFilledCallbacks = [];
        this.onRejectedCallbacks = [];

        try {
            executor(resolve, reject);
        } catch(err) {
            reject(err);
        }
    }
    // 4. MyPromise原型上的then方法
    then (onFulFilled, onRejected) {
        // log('then executor');
        // 处理无参数时的问题(值穿透)
        onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : function(value) {return value};
        // 抛出错误直接丢到下一个then中
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };

        let promise2;
        // 由于 promise.then 执行的时候promise对象已经是确定状态,从程序上说对回调函数进行同步调用也是行得通的。
        // 但是即使在调用 promise.then 注册回调函数的时候promise对象已经是确定的状态,
        // Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针。

        if(this.status === 'resolved'){
            promise2 = new MyPromise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let x = onFulFilled(this.data);
                        // log('onFulFilled return  value : '+x)
                        resolvePromsie(promise2, x, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                }, 0);
            })
        }
        if(this.status === 'rejected'){
            promise2 = new MyPromise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        // If either onFulfilled or onRejected returns a value x,
                         // run the Promise Resolution Procedure [[Resolve]](promise2, x).
                        let x = onRejected(this.reason);
                        resolvePromsie(promise2, x, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                }, 0)
            })
        }
        if(this.status === 'pending'){
            // 这里要做一件很有意思的事。。。。
            promise2 = new MyPromise((resolve, reject) => {
                // 将回调放在数组中等待调用
                this.onFulFilledCallbacks.push(() => {
                    process.nextTick(() => {
                        try {
                            let x = onFulFilled(this.data);
                            resolvePromsie(promise2, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    })
                  })
                this.onRejectedCallbacks.push(() => {
                    process.nextTick(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromsie(promise2, x, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }     
                    });
                })
            })
        }
        return promise2;
    }
    catch(onRejected){
        if(this.status === 'rejected'){
            promise2 = new MyPromise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        // If either onFulfilled or onRejected returns a value x,
                         // run the Promise Resolution Procedure [[Resolve]](promise2, x).
                        let x = onRejected(this.reason);
                        resolvePromsie(promise2, x, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                }, 0)
            })
        }
    }
    

}


function resolvePromsie(promise, x, resolve, reject){
        // 规范说先promise是不是来自同一个对象,并返回一个理由
        // If promise and x refer to the same object, reject promise with a TypeError as the reason.
        try {
            // If promise and x refer to the same object, reject promise with a TypeError as the reason.
            // 如果是相同引用就抛出错误
            // if(promise === x) return reject(new TypeError('不能循环引用'));
            // if x is an object or function,
            // 如果是对象,或是函数,我们就要看then是不是函数
            if(x != null && (typeof x === 'object' || typeof x === 'function')){
                // Let then be x.then
                let then = x.then;
                if(typeof then === 'function'){
                    // If then is a function, call it with x as this,
                    // first argument resolvePromise, and second argument rejectPromise, where:
                    // 执行then函数,拿到返回值
                    then.call(x, y => {
                        resolvePromsie(promise, y, resolve, reject);
                    }, (err) => {
                        reject(err);
                    })
                }else{
                    resolve(x);
                }
            }else{
                resolve(x);
            }
        } catch (err) {
            reject(err)
        }
    }



var mypromise = new MyPromise((resolve,reject) => {
    resolve('第一个异步任务 OK');
    // let timeOut = Math.random()*2;
    // setTimeout(function () {
 //        if (timeOut < 1) {
 //            resolve('第一个异步任务 OK');
 //        }
 //        else {
 //            reject('第一个异步任务 reject');
 //        }
 //    }, timeOut * 1000);
})
var mypromise2 = new MyPromise((resolve,reject) => {
    resolve('第二个异步任务');
})

// 测试
mypromise.then(function(data){
    log(data)
    return mypromise2;
},function(error){
    log(error)
    return mypromise2;
}).then().then().then(function(rd){
    log(rd);
   return 'test'
}).then(function(val){
    log(val);
}).then(function(val){
    log(val)
});



log('A'+111)
setTimeout(()=>{
    log('setTimeout 0ms')
})
setTimeout(()=>{
    log('setTimeout 10ms')
},10)

你可能感兴趣的:(promise)