相关文章推荐
不要命的芒果  ·  Object Lifecycle ...·  1 年前    · 
长情的啤酒  ·  Type 'string | ...·  2 年前    · 
咆哮的书签  ·  页面重载开启·  2 年前    · 

今天碰到一个面试问题,就是如果页面中有两个异步ajax的操作,因为不确定这两个异步操作的执行顺序,怎么在这两个操作都执行完再执行一个新的操作,最好的方法是什么?

我当时回答了方法一:嵌套两个ajax,在第二个ajax的返回函数中执行新的操作。面试官回复:这种方法太矬了。

于是想了下回答方法二:通过定时器setTimeout监听局部变量,确保两个异步操作执行完了再执行新操作。 面试官回复:这种方式性能不好,能不能想到一个简单又更合理的方法。

当时思考未果
所以把这个问题放上来寻求最好的方法是什么?欢迎讨论指点

1.Promise 包装异步ajax操作,
2.定义async 函数,
3.用await等待promise数据异步获取完成
这一种方法简洁高效,下面请看我专门给你写的示例代码
我懒得用ajax获取数据了,就用settimeout这个函数模拟获取数据吧,这个函数是异步的,原理效果一样。

//模拟ajax异步操作1
function ajax1() {
    const p = new Promise((resolve, reject) => {
        setTimeout(function() {
            resolve('ajax 1 has be loaded!')
        }, 1000)
    return p
//模拟ajax异步操作2
function ajax2() {
    const p = new Promise((resolve, reject) => {
        setTimeout(function() {
            resolve('ajax 2 has be loaded!')
        }, 2000)
    return p
//等待两个ajax异步操作执行完了后执行的方法
const myFunction = async function() {
    const x = await ajax1()
    const y = await ajax2()
        //等待两个异步ajax请求同时执行完毕后打印出数据
    console.log(x, y)
myFunction()

目前浏览器环境中开箱即用的原生方法是 Promise.all。

以调用我的地图库 Sinomap Demo 为例,这个页面中为了加载一张地图,需要多个同时发起但不能确保返回顺序的请求:

  • 中国地形数据

  • 各省份数值 JSON 数据

  • 多种图表叠加时多种图表存在多种 JSON 数据需通过不同数据接口返回……

  • 解决方法直接在未打包的 http://sinomap.ewind.us/demo/demo.js 中,示例:

    // 封装地形 GeoJSON 数据接口
    // 将每个数据接口封装为一个返回 Promise 的函数
    function getArea () {
      return new Promise((resolve, reject) => {
        fetch('./resources/china.json').then(resp =>
          resp.json().then(china => resolve(china))
    // 封装分色地图数据接口
    function getPopulation () {
      return new Promise((resolve, reject) => {
        fetch('./resources/china-population.json').then(resp =>
          resp.json().then(data => resolve(data))
    // 封装城市数据接口
    function getCity () {
      return new Promise((resolve, reject) => {
        fetch('./resources/city.json').then(resp =>
          resp.json().then(data => resolve(data))
    // 使用 Promise.all 以在三个数据接口均异步成功后,执行回调逻辑
    Promise.all([getArea(), getPopulation(), getCity()]).then(values => {
      // 依次从返回的数据接口数组中获取不同接口数据
      let china = values[0]
      let population = values[1]
      let city = values[2]
      // 使用数据
      doWithData(china, population, city)
    

    这样通过 Promise 不仅实现了回调逻辑的解耦,还实现了基础的异步流程控制。

    刚刚看到jquery 的 when 方法,所以给你重写了一个,不一定有jquery的那么好,但至少能实现效果了,可以在控制台直接输入下述代码试试,我勒个去,写了我整整半小时。。

    function ajax(callback){
        callback = callback || function(){};
        var xhr = new XMLHttpRequest();
        xhr.open("get","");
        xhr.onload = function(res){ callback(res) };
        xhr.send(null); 
    var when = (function(){
        var i = 0,
            len = 0,
            data = [];
        return function(array,callback){
            callback = callback || function(){};
           len = len || array.length;
            var fn = array.shift();
           fn(function(res){
                data.push(res);
                if(i < len){
                    when(array,callback);
                } else {
                    callback(data);
    })();
    when([ajax,ajax],function(data){
        console.log(data);
        processors.forEach((func, idx) => {
            func(function commit(asyncVal){ // 这就是commit函数 
                // 把 asyncVal 的所有属性合并到 o 上 
                // ( 利用 Object.keys 获取对象全部属性名 )
                Object.keys(asyncVal).forEach(key => {
                    o[key] = asyncVal[key]; 
                // 计数器自加 
                count++; 
                // 如果发射器全部发射完毕则调用回调函数 cb 并把 o 作为参数传递
                if (count === processors.length) cb(o); 
    

    执行异步发射器 并提供 最终回调

    launcher(todos, function(whereEczn){
        // todos 里面存放的异步操作的值由 commit 回调返回
        // 全部回调跑完的时候 就会执行当前这段函数 并把期望值返回
        console.log(whereEczn); 
        // 按顺序输出
        ['name', 'area'].forEach(key => {
            console.log(`${key}: ${whereEczn[key]}`); 
    

    https://eczn.coding.me/blog/%...