JS高阶(五)前端开发中的同步异步编程

132 阅读15分钟

1、前端开发中的同步异步编程

知识点

  • 进程与线程 & 浏览器中常用的线程
    • GUI渲染线程
    • JS引擎线程
    • 事件触发线程
    • 定时触发器线程
    • 异步HTTP请求线程
    • WebWorker
    • ....
  • JS是单线程异步编程
    • EventLoop
    • EventQueue
    • WebAPIS
    • 宏任务 macrotask [ˈmækroʊ]
    • 微任务 microtask [ˈmaɪkroʊ]

进程:一个程序

线程:这个程序中具体做事情的

同时做多件事情,需要多个线程;只有一个线程,一次只能做一件事,这件事干完才可以干下一个事情

进程中包含多个线程


同步编程:一次只能做一件事情,当前事件完成才能做下一个事情

异步编程:利用多线程机制,同时可以做多件事情

单线程如何实现异步编程?


浏览器 --- JS

浏览器打开一个页面,就会开辟一个进程,浏览器是多线程的;但他只分配了一个线程:JS引擎线程,用来渲染和解析js,所以js是单线程的;js中大部分代码都是同步编程


JS代码的执行是单线程的:因为浏览器只分配一个线程“JS引擎线程(主线程)”用来渲染和解析JS

  • JS中大部分代码都是同步的:如果此时主线程正在执行某些代码,那么其余的事情都做不了(例如:循环就是同步代码,如果JS中出现死循环,那么主线程永远空闲不下来,其他的事情都处理不了,整个页面就卡在这卡死了!!!)
  • JS中也有一部分代码是异步的:并不是像想象中的同时执行很多代码,而是“基于EventLoop&浏览器的多线程机制”实现出监听、排队的机制

为什么js是单线程

js作为主要运行在浏览器的脚本语言,js主要用途之一是操作DOM。

在js高程中举过一个例子,如果js同时有两个线程,同时对同一个dom进行操作,这时浏览器应该听哪个线程的,如何判断优先级?

为了避免这种问题,js必须是一门单线程语言,并且在未来这个特点也不会改变。

宏任务 macrotask微任务 microtask:优先级高
setTimeout/setIntervalrequestAnimationFrame「有争议」实现JS动画
事件绑定/队列Promise.then/catch/finally
XMLHttpRequest/Fetchasync/await
setImmediate「Node」queueMicrotask 创建一个新的异步微任务
MessageChannelprocess.nextTick「Node」
script整体代码块MutationObserver 监听DOM元素属性改变
IntersectionObserver 监听DOM元素和视口交叉的信息

script整体代码块是宏任务【同步的宏任务】

requestAnimationFrame

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

MutationObserver

MutationObserver接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。

IntersectionObserver

**IntersectionObserver**接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。

当一个IntersectionObserver对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦IntersectionObserver被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。

重绘&重排/回流

当DOM的变化引发了元素几何属性的变化,比如改变元素的宽高元素的位置,导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树,这个过程称为“重排”。完成重排后,要将重新构建的渲染树渲染到屏幕上,这个过程就是“重绘”。

简单的说,重排负责元素的几何属性更新,重绘负责元素的样式更新。而且,重排必然带来重绘,但是重绘未必带来重排。比如,改变某个元素的背景,这个就不涉及元素的几何属性,所以只发生重绘。

随堂练习题

setTimeout(() => {
 console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
 console.log(3);
}, 10);
console.log(4);
console.time('AA');
for (let i = 0; i < 90000000; i++) {
 // do soming
}
console.timeEnd('AA'); //=>AA: 79ms 左右
console.log(5);
setTimeout(() => {
 console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
 console.log(8);
}, 15);
console.log(9);

Snipaste_2021-08-23_00-22-17

Xnip2021-12-15_21-12-55


2、Promise解析

基于回调函数的方式封装ajax请求

ajax串行:多个ajax请求间存在依赖,只有上一个请求成功,才能发送下一个请求(例如:第二个请求的发送,需要依赖于第一个请求的结果)

ajax并行:请求之间没有啥依赖,可以同时发送多个请求「一般会额外处理一些事情:等待所有请求都成功,统一干啥事」

在没有promise之前,基于回调函数的方式管理ajax请求,在串行的需求中,很容易产生“回调地狱”

$.ajax({
    url: '/api/test1',
    method: 'GET',
    dataType: 'json',
    success(value) {
        console.log('第一个请求结果:', value);

        $.ajax({
            url: '/api/test2',
            success(value) {
                console.log('第二个请求结果:', value);

                $.ajax({
                    url: '/api/test3',
                    success(value) {
                        console.log('第三个请求结果:', value);
                    }
                });
            }
        });
    }
});

而promise是ES6中专门用来管理异步编程的,基于它可以避免回调地狱的问题

const http1 = () => {
    return new Promise(resolve => {
        $.ajax({
            url: '/api/test1',
            success: resolve
        });
    });
};
const http2 = () => {
    return new Promise(resolve => {
        $.ajax({
            url: '/api/test2',
            success: resolve
        });
    });
};
const http3 = () => {
    return new Promise(resolve => {
        $.ajax({
            url: '/api/test3',
            success: resolve
        });
    });
};

http1()
    .then(value => {
        console.log('第一个请求结果:', value);
        return http2();
    })
    .then(value => {
        console.log('第二个请求结果:', value);
        return http3();
    })
    .then(value => {
        console.log('第三个请求结果:', value);
    });

//语法糖
(async function () {
    let value = await http1();
    console.log('第一个请求结果:', value);

    value = await http2();
    console.log('第二个请求结果:', value);

    value = await http3();
    console.log('第三个请求结果:', value);
})();

Promise基础

Promise是ES6新增的内置类:

  • 不兼容IE浏览器「EDGE可以兼容的」:真实项目开发的时候,如果使用了promise,还要兼容IE浏览器,需要处理兼容

​ @1 babel/preset语法包,无法编译Promise内置类,它只能编译类似于let/const这种常规语法

​ @2 我们需要基于 @babel/polyfill 实现Promise的兼容处理「原理:基于ES5手动实现一个Promise」

  • 使用的时候是创建类的实例:实例具备私有属性、可以使用Promise.prototype上的方法、Promie作为对象存在一些静态的私有属性方法
let p1 = new Promise([executor])

[executor]

  • 必须是一个函数,传递的不是函数则会报错 Uncaught TypeError: Promise resolver xxx is not a function
  • new Promise的时候,会立即把[executor]函数执行「这步操作是同步编程」
  • [executor]函数执行的时候,会接受两个实参值,我们分别用resolve/reject形参来存储
    • resolve/reject存储的值也是函数
    • resolve([value]):把创建实例的状态改为成功态(fulfilled),实例的值是[value](成功的结果)
    • reject([reason]):把创建实例的状态改为失败态(rejected),实例的值是[reason](失败原因)
    • 如果[executor]函数执行过程中,有异常错误抛出,则实例的状态依然是rejected,实例的值是报错原因
    • 一但状态被改为fulfilled或者rejected,则不能再次修改了

实例p1具备的私有属性

  • [[PromiseState]]:"pending" 实例状态:pending(准备/默认状态) & fulfilled(成功态) & rejected(失败态)
  • [[PromiseResult]]:undefined 实例的值(结果):存储成功的结果或者失败的原因,状态为pending则默认值是undefined
  • new Promise的时候会立即执行executor函数 同步
let p1 = new Promise(function executor(resolve,reject){
    resolve(100);//立即修改实例的状态和值
    reject(100);
})
new Promise产生的实例,状态是成功还是失败,是由executor执行是否报错{执行报错,则实例是失败态}、以及resolve还是reject执行决定
  • Promise.resolve(100):直接创建状态是成功的实例

  • Promise.reject(100):直接创建状态是失败的实例

Promise.prototype 为实例提供的公共属性方法

  • catch
  • finally
  • Symbol(Symbol.toStringTag): "Promise"
  • then
实例.then(onfulfilled,onrejected)
  • @1 首先观察实例的状态,如果此时已经知道实例是成功或者失败的状态

    • 创建一个异步“微任务”【放在WebAPI中去监听,监听的时候知道它的状态,所以直接把执行的哪个方法,挪至到EventQueue中排队等着】
    • 状态是成功的,后期执行的是onfulfilled
    • 状态是失败的,后期执行的是onrejected
    console.log(1);
    let p1 = new Promise(function executor(resolve, reject) {
        console.log(2);
        resolve(100); //立即修改实例的状态和值
        // reject(0);
        console.log(3);
    });
    console.log(4);
    p1.then(function onfulfilled(value) {
        console.log('成功:', value);
    }, function onrejected(reason) {
        console.log('失败:', reason);
    });
    console.log(5);
    // 答案:1 2 3 4 5 成功:100 
    
  • @2 如果实例此时的状态还是pending

    • 把onfulfilled/onrejected先存储到指定的容器中【放在WebAPI中监听状态的改变】
    • 当后期执行resolve/reject的时候
      • 立即修改实例的状态和值【同步】
      • 创建一个异步微任务,后面让指定容器中的方法执行【挪至EventQueue中排队等待】
    console.log(1);
    let p1 = new Promise(resolve => {
        console.log(2);
        setTimeout(() => {
            console.log(3);
            resolve(100);
            console.log(4);
        }, 1000);
        console.log(5);
    });
    console.log(6);
    p1.then(value => {
        console.log('成功:', value);
    }, reason => {
        console.log('失败:', reason);
    });
    console.log(7);
    // 答案:1 2 5 6 7 ...过1S后... 3 4 成功:100 
    

    @1 已经明确知道promise的状态是成功还是失败的了,它也不会立即执行onfulfilled/onrejected,而是创建一个“异步的微任务”「WebAPI -> EventQueue -> 同步执行完,去EventQueue中查找执行」

    @2 此时的promise的状态还是pending,则先把onfulfilled/onrejected存储起来「也可以理解为放在WebAPI中监听」;后续当我们基于resolve/reject把实例的状态“立即”修改后,也并不会立即去通知onfulfilled/onrejected执行,而是创建一个“异步微任务”「也可以理解为把WebAPI中的任务挪至到EventQueue中排队去等待了」;

“实例.then”会返回一个全新的promise实例【p2】,这个实例的成功失败,有p1管控的onfulfilled或者onrejected不论哪个方法执行决定
  • 方法执行是否返回新的promise实例,如果没有返回:
    • 方法执行只要不报错,则p2就是成功的,值就是函数返回值
    • 执行报错,则p2就是失败的,值是报错原因
  • 如果返回的是新的promise实例【new-promise】,则new-promise的状态和值决定了p2的状态和值
let p1 = Promise.resolve(100);
let p2 = p1.then(value => {
    console.log('成功:', value);
    return Promise.reject(value * 10);
}, reason => {
    console.log('失败:', reason);
    return reason / 10;
});
// 因为p1管理的onfulfilled方法还在EventQueue中排队等待执行,所以此时p2的状态还是pending
//  + 把onfulfilled/onrejected存储起来,放在WebAPI监听,监听p2状态的改变「把p1管控的onfulfilled执行就知道了」
//  + 当p1对应的onfulfilled执行后,把p2变为失败,此时把p2对应的onrejected挪至到EventQueue中排队等着执行...
p2.then(value => {
    console.log('成功:', value);
}, reason => {
    console.log('失败:', reason);
});
//------------------------------------------
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(100);
    }, 1000);
}); 
p1.then(value => {
    console.log('成功:', value);
}).catch(reason => { //.catch === .then(null,onrejected)
    console.log('失败:', reason);
});

**“THEN链的穿透和顺延机制”:**执行then,如果onfulfilled/onrejected没有传递,则顺延到下一个同等状态的方法上

promise实例.catch([onrejected]) : 等同于 promise实例.then(null,[onrejected])

  • 如果实例状态是失败,而我们也没有去设置onrejected进行处理,那么控制台会“抛异常”,但是不影响其余的代码执行!!
  • 所有我们会在THEN链的最末尾设置一个catch,这样不论哪一级返回失败的promise实例,都会把catch中的onrejected执行
Promise.reject(100).then(value => {
    console.log('成功:', value);
    return value / 10;
}).then(value => {
    console.log('成功:', value);
    return value / 10;
}).catch(reason => {
    console.log('失败:', reason);
}).finally(()=>{

});

Promise 作为普通对象,具备静态私有属性方法

  • all
  • allSettled
  • any
  • race
  • reject:Promise.reject(xxx) 创建一个状态为rejected、值是xxx的promise实例
  • resolve:Promise.resolve(xxx) 创建一个状态为fulfilled、值是xxx的promise实例
  • ...

Promise.all/any/race([promises])

  • [promises]是包含零到多个promise实例的集合,如果其中某一项不是promise实例,则浏览器也会默认把其变为状态是fulfilled、值是自身的实例;返回结果都是一个新的promise实例「例如:@P」!!
  • all:[promises]集合中每一项都是成功的,@P就是成功的,值是按照原始集合顺序,依次存储每一个实例的值;只要有一项是失败的,则@P就是失败的,值是此失败项的原因,后续还没处理的实例也不用再处理了!!
  • any:集合中是要有一项是成功的,则@P就是成功的,值是成功项的值;所有项都是失败的,@P才是失败的!!
  • race:看集合中谁最先处理完成,那么就按照他的结果作为@P的结果,哪怕是失败态!!
let p1 = new Promise(resolve => {
    setTimeout(() => {
        resolve(1);
    }, 2000);
});
let p2 = Promise.reject(2);
let p3 = 3; //-> Promise.resolve(3)
Promise.race([p1, p2, p3]).then(value => {
    console.log('成功:', value);
}).catch(reason => {
    console.log('失败:', reason);
});

async/await:是promise+genreatar的语法糖

  • async修饰的函数,默认返回值会变为一个promise实例
  • 必须在函数中使用await,而且当前函数需要经过async修饰
  • async:用来修饰函数
    • 让函数返回的结果是一个promise实例
    • 如果在函数中使用await,则函数必须经过async的修饰
  • await:可以把异步操作改为类似于同步的效果
    • await后面必须跟一个promise实例,如果不是也会变为一个实例(状态是fulfilled、值是本身)
    • 需要等待后面的promise实例状态为成功,才会执行”当前上下文“await下面的代码;如果后面实例是失败的,则下面代码就不执行了!
    • await后面的实例是失败的,我们可以基于try/catch实现异常捕获,在catch中处理状态是失败要做的事情
async function fn() {
     return 1;
 }
 console.log(fn()); //promise实例: 状态fulfilled 值1

 async function fn() {
     console.log(num);
     return 1;
 }
 console.log(fn()); //状态rejected 值是报错原因

 async function fn() {
     return Promise.reject('NO');
 }
 console.log(fn()); //返回值本身是promise实例,则以自己返回为主
//--------------------------------------------------------------
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(100);
    }, 1000);
});
(async function () {
    // await 后面放置的是promise实例「如果不是,则默认变为状态是成功,值是这个值的promise实例」
    //   + 把当前上下文中,await“下面的代码”作为一个“异步微任务”,放在WebAPI中去监听,监听实例的状态
    //   + 如果状态是成功,把其“下面的代码执行”挪至到EventQueue中排队等着 
    //   + 如果状态是失败,则下面代码不会执行
    let value = await p1;
    console.log('成功:', value);
})();

(async function () {
    // 捕获await后面实例是失败的?
    try {
        let value = await p1;
        console.log('成功:', value);
    } catch (err) {
        console.log('失败:', err);
    }
})();

3、随堂练习题 & 面试题

async function async1() {
 console.log('async1 start');
 await async2();
 console.log('async1 end');
}
async function async2() {
 console.log('async2');
}
console.log('script start');
setTimeout(function() {
 console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
 console.log('promise1');
 resolve();
}).then(function() {
 console.log('promise2');
});
console.log('script end');

2

let body = document.body;
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(1);
    });
    console.log(2);
});
body.addEventListener('click', function () {
    Promise.resolve().then(() => {
        console.log(3);
    });
    console.log(4);
});

3

console.log('start');
let intervalId;
Promise.resolve().then(() => {
 console.log('p1');
}).then(() => {
 console.log('p2');
});
setTimeout(() => {
 Promise.resolve().then(() => {
     console.log('p3');
 }).then(() => {
     console.log('p4');
 });
 intervalId = setInterval(() => {
     console.log('interval');
 }, 3000);
 console.log('timeout1');
}, 0);

4

WechatIMG1559


setTimeout(() => {
 console.log('a');
});
Promise.resolve().then(() => {
console.log('b');
}).then(() => {
return Promise.resolve('c').then(data => {
     setTimeout(() => {
        console.log('d')
     });
    console.log('f');
     return data;
});
}).then(data => {
console.log(data);
});

5

WechatIMG1561


function func1() {
 console.log('func1 start');
return new Promise(resolve => {
     resolve('OK');
});
}
function func2() {
 console.log('func2 start');
return new Promise(resolve => {
     setTimeout(() => {
        resolve('OK');
     }, 10);
});
}
console.log(1);
setTimeout(async () => {
console.log(2);
 await func1();
console.log(3);
}, 20);
for (let i = 0; i < 90000000; i++) {} //循环大约要进行80MS左右
console.log(4);
func1().then(result => {
 console.log(5);
});
func2().then(result => {
console.log(6);
});
setTimeout(() => {
 console.log(7);
}, 0);
console.log(8);

6

WechatIMG1570


4、Promise源码解析

new.target 检测是否为new执行

  • 是new执行,获取的是构造函数
  • 如果不是,则为undefined

微信图片_20211220001740

queueMicrotask 创建一个异步微任务

queueMicrotask(function () {
   onfulfilled(self.result);
});

Promise.all()中必须是一个迭代器,如果不是会报错;

Set,NodeList,HTMLCollection中都有Symbol.toStringTag 这个属性;

微信图片_20211221222416

如果为Set集合,获取长度使用size

微信图片_20211221222422

(function() {
    /* 核心部分 */
    function Promise(executor) {
        var self = this,
            change;

        // 保证executor是个函数 & 是基于new Promise执行的
        if (typeof executor !== "function") throw new TypeError("Promise resolver is not a function");
        if (!(self instanceof Promise)) throw new TypeError("undefined is not a promise");

        // 实例设置状态和值
        self.state = "pending";
        self.result = undefined;
        self.onfulfilledCallbacks = [];
        self.onrejectedCallbacks = [];
        change = function change(state, result) {
            if (self.state !== "pending") return;
            self.state = state;
            self.result = result;
            // 异步通知事先存储的指定方法执行
            setTimeout(function() {
                var callbacks = state === 'fulfilled' ? self.onfulfilledCallbacks : self.onrejectedCallbacks;
                callbacks.forEach(function(callback) {
                    if (typeof callback !== "function") return;
                    callback(self.result);
                })
            });
        }

        // 立即执行executor,并且监听是否报错
        try {
            executor(function resolve(value) {
                change('fulfilled', value);
            }, function reject(reason) {
                change('rejected', reason);
            })
        } catch (err) {
            change('rejected', err);
        }
    }

    /* 原型部分 */
    var resolvePromise = function resolvePromise(promise, x, resolve, reject) {
        // 根据onfulfilled/onrejected执行的返回结果x,去验证.then返回的实例promise是成功还是失败「基于执行resolve/reject」
        if (promise === x) throw new TypeError("Chaining cycle detected for promise #<Promise>");
        if (x !== null && /^(object|function)$/.test(typeof x)) {
            var then;
            // 避免x.then因做了数据劫持,导致访问可能会报错,所以做个特殊处理
            try {
                then = x.then;
            } catch (err) {
                reject(err);
            }
            if (typeof then === "function") {
                //有一个程序正在处理,其他程序就不处理
                var called = false;
                // 说明x是一个新的promise实例:x的成功/失败,决定了promise的成功或者失败
                try {
                    then.call(
                        x,
                        function onfulfilled(y) {
                            if (called) return;
                            called = true;
                            resolvePromise(promise, y, resolve, reject);
                        },
                        function onrejected(r) {
                            if (called) return;
                            called = true;
                            reject(r)
                        })
                } catch (err) {
                    if (called) return;
                    called = true;
                    reject(err);
                }
            }
        }
        // 说明onfulfilled/onrejected方法执行返回的结果x并不是一个实例:让promise状态是fulfilled、值就是函数返回值x
        resolve(x);
    }
    Promise.prototype = {
        constructor: Promise,
        then: function(onfulfilled, onrejected) {
            var self = this,
                promise;
            if (!(self instanceof Promise)) throw new TypeError("Method Promise.prototype.then called on incompatible receiver #<Promise>");
            // 实现THEN链的穿透机制
            if (typeof onfulfilled !== "function") {
                onfulfilled = function onfulfilled(value) {
                    return value;
                }
            }
            if (typeof onrejected !== "function") {
                onrejected = function onrejected(reason) {
                    throw reason;
                }
            }

            // 每一次执行TEHN会返回一个全新的实例
            promise = new Promise(function(resolve, reject) {
                // 验证执行THEN的时候,实例的状态是否已知
                //   + 已知状态:创建异步微任务,执行onfulfilled/onrejected中的某个方法
                //   + 未知状态:把onfulfilled/onrejected存储到指定的容器中,当实例状态发生改变,再通知指定方法执行「异步」
                switch (self.state) {
                    case "fulfilled":
                        setTimeout(function() {
                            try {
                                var x = onfulfilled(self.result);
                                resolvePromise(promise, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    case "rejected":
                        setTimeout(function() {
                            try {
                                var x = onrejected(self.result);
                                resolvePromise(promise, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        break;
                    default:
                        self.onfulfilledCallbacks.push(function(value) {
                            try {
                                var x = onfulfilled(value);
                                resolvePromise(promise, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                        self.onrejectedCallbacks.push(function(reason) {
                            try {
                                var x = onrejected(reason);
                                resolvePromise(promise, x, resolve, reject);
                            } catch (err) {
                                reject(err);
                            }
                        });
                }
            })
        },
        catch: function myCatch(onrejected) {
            //this.then(null,onrejected); 如果this如果不是promise实例,没有then方法会报错
            return Promise.prototype.then.call(this, null, onrejected);
        }
    };

    /* 为Promise原型添加[Symbol.toStringTag]属性 */
    if (typeof Symbol !== "undefined") Promise.prototype[Symbol.toStringTag] = "Promise";

    /* 静态属性 */
    var isPromise = function isPromise(x) {
        if (x !== null && /^(object|function)$/.test(typeof x)) {
            var then;
            try {
                then = x.then;
            } catch (_) {
                return false;
            }
            if (typeof then === "function") return true;
        }
        return false;
    }

    var checkIterator = function checkIterator(arr) {
        if (arr == null) return false;
        if (typeof Symbol !== "undefined") {
            var iterator = arr[Symbol.iterator];
            return iterator ? true : false;
        }
        return Array.isArray(arr);
    }

    // 以实例为准
    Promise.resolve = function resolve(value) {
        if (isPromise(value)) return value;
        return new Promise(function(resolve) {
            resolve(value);
        })
    }

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

    Promise.all = function all(promises) {
        if (!checkIterator(promises)) throw TypeError("promises is not iterator");
        var n = 0,
            results = [],
            len = promises.length || promises.size;
        return new Promise(function(resolve, reject) {
            for (var i = 0; i++; i < len) {
                (function(i) {
                    var promise = promises[i];
                    if (!isPromise(promise)) promise = Promise.resolve(promise);
                    promise.then(function onfulfilled(value) {
                        // 某一项成功,则把当前成功项的值记录到result中(索引和promise中的索引保持一致)
                        results[i] = value;
                        // 都成功,最后整体返回成功
                        n++;
                        if (n >= len) resolve(results);
                    }, function onrejected(reason) {
                        // 只要有一项是失败的,则整体返回失败,值是当前失败项的值
                        reject(reason);
                    })
                })(i);
            }
        })
    }

    Promise.any = function any(promises) {
        if (!checkIterator(promises)) throw TypeError("promises is not iterator");
        var n = 0,
            len = promises.length || promises.size;
        return new Promise(function(resolve, reject) {
            for (var i = 0; i++; i < len) {
                (function(i) {
                    var promise = promises[i];
                    if (!isPromise(promise)) promise = Promise.resolve(promise);
                    promise.then(function onfulfilled(value) {
                        resolve(value);
                    }, function onrejected(reason) {
                        n++;
                        if (n >= len) reject('All promises were rejected')
                    })
                })(i);
            }
        })
    }



    /* 暴露API */
    if (typeof module !== "object" && module.exports !== "object") { module.exports = Promise }
    if (window !== "undefined") window.Promise = Promise;
})();

var p1 = new Promise(function(resolve, reject) {
    resolve(100);
})

p1.then(function(value) {

}, function(reason) {

})

字节跳动面试题:当失败的等于指定的值,再输出状态为onrejected,结果为失败的值

// 字节跳动面试题:当失败的等于指定的值,再输出状态为onrejected,结果为失败的值
Promise.all = function all(promises, limit) {
    if (!checkIterator(promises)) throw new TypeError("promises is not iterable");
    if (typeof limit !== "number" || isNaN(limit)) limit = 1;
    var n = 0,
        m = 0,
        results = [],
        reasons = [],
        len = promises.length || promises.size;
    limit = limit < 1 ? 1 : (limit > len ? len : limit);
    return new Promise(function(resolve, reject) {
        for (var i = 0; i < len; i++) {
            (function(i) {
                var promise = promises[i];
                if (!isPromise(promise)) promise = Promise.resolve(promise);
                promise.then(function resolve(value) {
                    n++;
                    results[i] = value;
                    if (n >= len) resolve(results);
                }, function reject(reason) {
                    m++;
                    reasons[i] = reason;
                    if (m >= limit) {
                        reject(limit === 1 ? reason : reasons);
                        return;
                    }
                    n++;
                    results[i] = null;
                })
            })(i);
        }
    })
}