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)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容